in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs [272:367]
public void TestLongTryExecute()
{
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;
const string stackTraceHeader = "CurrentProcessThreadDumps:";
var executionWasNotCancelledByTimeoutReceived = false;
Lifetime.Using(lifetime =>
{
void LoggerHandler(LeveledMessage message)
{
if (message.Level == LoggingLevel.WARN && message.FormattedMessage.Contains(expectedWarningText))
warningReceived = true;
}
lifetime.Bracket(
() => TestLogger.ExceptionLogger.Handlers += LoggerHandler,
() => TestLogger.ExceptionLogger.Handlers -= LoggerHandler
);
var lifetimeDefinition = lifetime.CreateNested();
var def2 = lifetime.CreateNested();
LifetimeDefinition.AdditionalDiagnostics = new LifetimeDefinition.AdditionalDiagnosticsInfo(false, async (lf) =>
{
var stacks = GetCurrentProcessThreadDumps();
Assert.AreEqual(lifetimeDefinition.Lifetime, lf);
executionWasNotCancelledByTimeoutReceived = true;
return $"{stackTraceHeader}\n{stacks}";
});
def2.Terminate();
Assert.IsFalse(executionWasNotCancelledByTimeoutReceived);
var lifetimeTerminatedEvent = new ManualResetEvent(false);
var backgroundThreadIsInTryExecuteEvent = new ManualResetEvent(false);
var thread = new Thread(() => lifetimeDefinition.Lifetime.TryExecute(() =>
{
backgroundThreadIsInTryExecuteEvent.Set();
WaitForLifetimeTerminatedEvent(lifetimeTerminatedEvent);
}));
thread.Start();
backgroundThreadIsInTryExecuteEvent.WaitOne();
lifetimeDefinition.Terminate();
lifetimeTerminatedEvent.Set();
thread.Join();
try
{
TestLogger.ExceptionLogger.ThrowLoggedExceptions();
}
catch (Exception e)
{
if (!e.Message.Contains(expectedExceptionText))
throw;
receivedException = e;
}
});
Assert.IsTrue(warningReceived, "Warning `{0}` must have been logged", expectedWarningText);
Assert.IsNotNull(receivedException, "Exception `{0}` must have been logged", expectedExceptionText);
Assert.IsTrue(executionWasNotCancelledByTimeoutReceived);
Assert.IsTrue(receivedException.Message.Contains(stackTraceHeader), $"Exception `{expectedExceptionText}` doesn't contain {stackTraceHeader}");
Assert.IsTrue(receivedException.Message.Contains(nameof(WaitForLifetimeTerminatedEvent)), $"Exception `{expectedExceptionText}` doesn't contain {nameof(WaitForLifetimeTerminatedEvent)} method");
static string GetCurrentProcessThreadDumps()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// clrmd crashes the process if os is not Windows, so just return the name of the method
return nameof(WaitForLifetimeTerminatedEvent);
}
using var dataTarget = DataTarget.AttachToProcess(Process.GetCurrentProcess().Id, suspend: false);
var clrVersion = dataTarget.ClrVersions.SingleOrDefault() ?? throw new Exception("Failed to get single clr from current process");
using var runtime = clrVersion.CreateRuntime();
var output = new StringBuilder();
foreach (var clrThread in runtime.Threads)
{
if (!clrThread.IsAlive)
continue;
output.AppendLine($"Thread #{clrThread.ManagedThreadId}:");
foreach (var frame in clrThread.EnumerateStackTrace())
output.AppendLine($"\tat {frame}");
}
return output.ToString();
}
}