in rd-net/Lifetimes/Lifetimes/LifetimeDefinition.cs [516:585]
private void Destruct()
{
var status = Status;
if (Mode.IsAssertion) Assertion.Assert(status == LifetimeStatus.Terminating, "{0}: bad status for destructuring start", this);
if (Mode.IsAssertion) Assertion.Assert(ourMutexSlice[myState] == false, "{0}: mutex must be released in this point", this);
//no one can take mutex after this point
var resources = myResources!;
if (Mode.IsAssertion) Assertion.AssertNotNull(resources, "{0}: `resources` can't be null on destructuring stage", this);
for (var i = myResCount - 1; i >= 0; i--)
{
try
{
switch (resources[i])
{
case Action a:
a();
break;
case LifetimeDefinition ld:
ld.Terminate();
break;
case IDisposable d:
d.Dispose();
break;
case ITerminationHandler th:
th.OnTermination(Lifetime);
break;
default:
Log.Error("{0}: unknown type of termination resource: {1}", this, resources[i]);
break;
}
}
catch (Exception e)
{
Log.Error(e, $"{this}: exception on termination of resource[{i}]: ${resources[i]}");
}
}
myResources = null;
myResCount = 0;
//In fact we shouldn't make it, because it should provide stable CancellationToken to finish enclosing tasks in Canceled state (not Faulted)
//But to avoid memory leaks we must do it. So if you 1) run task with alive lifetime 2) terminate lifetime 3) in task invoke ThrowIfNotAlive() you can obtain `Faulted` state rather than `Canceled`. But it doesn't matter in `async-await` programming.
if (!ReferenceEquals(this, Terminated))
{
// we need to cancel myCts because a race condition is possible
// Thread 1 MarkCancelingRecursively -> Exchange status Alive to Cancelling
// Thread 2 Terminate -> (skip MarkCancelingRecursively because Status > Alive) -> Destruct -> myCts = Terminated.myCts
// Thread 1 myTcs?.Cancel() -> the original cts will never be cancelled
// so we have to cancel it here
if (myCts is { } cts)
cts.Cancel();
// synchronization is not needed here, because the creation of cts happens only under UnderMutexCookie(Alive)
myCts = Terminated.myCts;
}
var statusIncrementedSuccessfully = IncrementStatusIfEqualsTo(LifetimeStatus.Terminating);
myId = null;
if (Mode.IsAssertion) Assertion.Assert(statusIncrementedSuccessfully, "{0}: bad status for destructuring finish", this);
}