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

Log error on accessing non-initialized lifetime #504

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions rd-net/Lifetimes/Lifetimes/Lifetime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,42 @@ public enum LifetimeTerminationTimeoutKind
{

private readonly LifetimeDefinition? myDefinition;
internal LifetimeDefinition Definition => myDefinition ?? LifetimeDefinition.Eternal;

internal LifetimeDefinition Definition
{
get
{
var def = myDefinition;
if (def != null) return def;

Assertion.Assert(IsUninitialized);

AssertInitialized();
return LifetimeDefinition.Eternal;
}
}

internal void AssertInitialized()
{
if (!Mode.IsAssertion) return;

if (LogErrorIfLifetimeIsNotInitialized && IsUninitialized)
{
Log.Root.Error("Lifetime is not initialized. " +
"This may cause a memory leak as default(Lifetime) is treated as Eternal. " +
"Please provide a properly initialized Lifetime or use `Lifetime?` if you need to handle both cases. " +
"Use Lifetime.Eternal explicitly if that behavior is intended.");
}
}

//ctor
internal Lifetime(LifetimeDefinition definition)
{
myDefinition = definition;
}

[PublicAPI]
public static bool LogErrorIfLifetimeIsNotInitialized = true;


#if !NET35
/// <summary>
Expand Down Expand Up @@ -147,6 +175,17 @@ internal Lifetime(LifetimeDefinition definition)
/// Whether current lifetime is equal to <see cref="Eternal"/> and never be terminated
/// </summary>
[PublicAPI] public bool IsEternal => Definition.IsEternal;


/// <summary>
/// Whether current lifetime is not properly initialized (created by default(Lifetime))
/// </summary>
[PublicAPI] public bool IsUninitialized => !IsInitialized;

/// <summary>
/// Whether current lifetime is properly initialized
/// </summary>
[PublicAPI] public bool IsInitialized => myDefinition != null;

/// <summary>
/// Is <see cref="Status"/> of this lifetime equal to <see cref="LifetimeStatus.Alive"/>
Expand Down
11 changes: 8 additions & 3 deletions rd-net/Lifetimes/Lifetimes/LifetimeDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -460,16 +460,21 @@ public void Terminate()
+ "This may happen either because of the ExecuteIfAlive failed to complete in a timely manner. In the case there will be following error messages." + Environment.NewLine
+ "This is also possible if the thread waiting for the termination wasn't able to receive execution time during the wait in SpinWait.spinUntil, so it has missed the fact that the lifetime was terminated in time.");

ourLogErrorAfterExecution.InterlockedUpdate(ref myState, true);
#if !NET35
if (AdditionalDiagnostics is { } additionalDiagnostics)
{
Log.Catch(() =>
try
{
ourAdditionalDiagnosticsStorage.AddData(this, additionalDiagnostics.GetAdditionalDiagnosticsIfExecutionWasNotCancelledByTimeoutAsync(Lifetime));
});
}
catch (Exception e)
{
Log.Error(e);
}
}
#endif

ourLogErrorAfterExecution.InterlockedUpdate(ref myState, true);
}

if (!IncrementStatusIfEqualsTo(LifetimeStatus.Canceling))
Expand Down
4 changes: 3 additions & 1 deletion rd-net/Lifetimes/Lifetimes/ValueLifetimed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ public void Deconstruct(out Lifetime lifetime, out T? value)

public ValueLifetimed(Lifetime lifetime, T value)
{
lifetime.AssertInitialized();

Lifetime = lifetime;
Value = value;
}

public void ClearValueIfNotAlive()
{
if (!Lifetime.IsAlive)
if (Lifetime is { IsInitialized: true, IsAlive: false })
Value = default(T);
}
}
Expand Down
25 changes: 17 additions & 8 deletions rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,25 @@
[Test]
public void TestEquals()
{
Lifetime eternal = default;
Assert.AreEqual(Lifetime.Eternal, eternal);
Assert.AreEqual(Lifetime.Eternal, Lifetime.Eternal);
Assert.AreEqual(eternal, eternal);
var old = Lifetime.LogErrorIfLifetimeIsNotInitialized;
Lifetime.LogErrorIfLifetimeIsNotInitialized = false;
try
{
Lifetime eternal = default;
Assert.AreEqual(Lifetime.Eternal, eternal);
Assert.AreEqual(Lifetime.Eternal, Lifetime.Eternal);
Assert.AreEqual(eternal, eternal);

Assert.True(Lifetime.Eternal == eternal);
Assert.True(Lifetime.Eternal == eternal);

Assert.AreNotEqual(Lifetime.Eternal, Lifetime.Terminated);
Assert.False(Lifetime.Eternal == Lifetime.Terminated);
Assert.False(eternal == Lifetime.Terminated);
Assert.AreNotEqual(Lifetime.Eternal, Lifetime.Terminated);
Assert.False(Lifetime.Eternal == Lifetime.Terminated);
Assert.False(eternal == Lifetime.Terminated);
}
finally
{
Lifetime.LogErrorIfLifetimeIsNotInitialized = old;
}
}

[Test]
Expand Down Expand Up @@ -223,7 +232,7 @@
const string expectedWarningText = "can't wait for `ExecuteIfAlive` completed on other thread";
const string expectedExceptionText = "ExecuteIfAlive after termination of";
var warningReceived = false;
Exception? receivedException = null;

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (macos-13)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (macos-13)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (macos-13)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 235 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (macos-13)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

#if !NET35
const string stackTraceHeader = "CurrentProcessThreadDumps:";
Expand All @@ -248,7 +257,7 @@

var def2 = lifetime.CreateNested();
#if !NET35
LifetimeDefinition.AdditionalDiagnostics = new LifetimeDefinition.AdditionalDiagnosticsInfo(false, async (lf) =>

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (macos-13)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (macos-13)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (macos-13)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 260 in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs

View workflow job for this annotation

GitHub Actions / build (macos-13)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
var stacks = GetCurrentProcessThreadDumps();
Assert.AreEqual(lifetimeDefinition.Lifetime, lf);
Expand Down
Loading