|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Diagnostics;
using System.Threading;
namespace Microsoft.AspNetCore.Cryptography;
internal static class WeakReferenceHelpers
{
public static T GetSharedInstance<T>(ref WeakReference<T>? weakReference, Func<T> factory)
where T : class, IDisposable
{
// First, see if the WR already exists and points to a live object.
WeakReference<T>? existingWeakRef = Volatile.Read(ref weakReference);
T? newTarget = null;
WeakReference<T>? newWeakRef = null;
while (true)
{
if (existingWeakRef != null)
{
if (weakReference!.TryGetTarget(out var existingTarget))
{
// If we created a new target on a previous iteration of the loop but we
// weren't able to store the target into the desired location, dispose of it now.
newTarget?.Dispose();
return existingTarget;
}
}
// If the existing WR didn't point anywhere useful and this is our
// first iteration through the loop, create the new target and WR now.
if (newTarget == null)
{
newTarget = factory();
Debug.Assert(newTarget != null);
newWeakRef = new WeakReference<T>(newTarget);
}
Debug.Assert(newWeakRef != null);
// Try replacing the existing WR with our newly-created one.
WeakReference<T>? currentWeakRef = Interlocked.CompareExchange(ref weakReference, newWeakRef, existingWeakRef);
if (ReferenceEquals(currentWeakRef, existingWeakRef))
{
// success, 'weakReference' now points to our newly-created WR
return newTarget;
}
// If we got to this point, somebody beat us to creating a new WR.
// We'll loop around and check it for validity.
Debug.Assert(currentWeakRef != null);
existingWeakRef = currentWeakRef;
}
}
}
|