|
// 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.Collections.Concurrent;
using Microsoft.Build.Framework;
#nullable disable
#if BUILD_ENGINE
using Microsoft.Build.Shared;
namespace Microsoft.Build.BackEnd.Components.Caching
#else
namespace Microsoft.Build.Shared
#endif
{
/// <summary>
/// This is a cache of objects which are registered to be disposed of at a specified time.
/// </summary>
internal class RegisteredTaskObjectCacheBase
{
/// <summary>
/// The cache for AppDomain lifetime objects.
/// </summary>
private static Lazy<ConcurrentDictionary<object, object>> s_appDomainLifetimeObjects = new Lazy<ConcurrentDictionary<object, object>>();
/// <summary>
/// The cache for Build lifetime objects.
/// </summary>
private Lazy<ConcurrentDictionary<object, object>> _buildLifetimeObjects = new Lazy<ConcurrentDictionary<object, object>>();
#if FEATURE_APPDOMAIN
/// <summary>
/// Static constructor which registers a callback to dispose of AppDomain-lifetime cache objects.
/// </summary>
static RegisteredTaskObjectCacheBase()
{
AppDomain.CurrentDomain.DomainUnload += new EventHandler((sender, args) =>
{
DisposeObjects(s_appDomainLifetimeObjects);
});
}
#endif
#region IRegisteredTaskObjectCache
/// <summary>
/// Disposes of all of the cached objects registered with the specified lifetime.
/// </summary>
public void DisposeCacheObjects(RegisteredTaskObjectLifetime lifetime)
{
var lazyCollection = GetLazyCollectionForLifetime(lifetime);
DisposeObjects(lazyCollection);
}
/// <summary>
/// Registers a task object with the specified key and lifetime.
/// </summary>
public void RegisterTaskObject(object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
{
ConcurrentDictionary<object, object> dict = GetCollectionForLifetime(lifetime, dontCreate: false);
dict?.TryAdd(key, obj);
}
/// <summary>
/// Gets a previously registered task object.
/// </summary>
public object GetRegisteredTaskObject(object key, RegisteredTaskObjectLifetime lifetime)
{
ConcurrentDictionary<object, object> dict = GetCollectionForLifetime(lifetime, dontCreate: true);
object obj = null;
dict?.TryGetValue(key, out obj);
return obj;
}
/// <summary>
/// Unregisters a previously registered task object.
/// </summary>
public object UnregisterTaskObject(object key, RegisteredTaskObjectLifetime lifetime)
{
ConcurrentDictionary<object, object> dict = GetCollectionForLifetime(lifetime, dontCreate: true);
object obj = null;
dict?.TryRemove(key, out obj);
return obj;
}
#endregion
/// <summary>
/// Returns true if a collection is not yet created or if it has no content.
/// </summary>
protected bool IsCollectionEmptyOrUncreated(RegisteredTaskObjectLifetime lifetime)
{
var collection = GetCollectionForLifetime(lifetime, dontCreate: true);
return (collection == null) || collection.IsEmpty;
}
/// <summary>
/// Returns the collection associated with a particular lifetime.
/// </summary>
protected ConcurrentDictionary<object, object> GetCollectionForLifetime(RegisteredTaskObjectLifetime lifetime, bool dontCreate)
{
Lazy<ConcurrentDictionary<object, object>> dict = GetLazyCollectionForLifetime(lifetime);
// If we aren't supposed to create it, don't force the creation.
if (dontCreate && !dict.IsValueCreated)
{
return null;
}
return dict.Value;
}
/// <summary>
/// Gets the lazy cache for the specified lifetime.
/// </summary>
protected Lazy<ConcurrentDictionary<object, object>> GetLazyCollectionForLifetime(RegisteredTaskObjectLifetime lifetime)
{
Lazy<ConcurrentDictionary<object, object>> dict = null;
switch (lifetime)
{
case RegisteredTaskObjectLifetime.Build:
dict = _buildLifetimeObjects;
break;
case RegisteredTaskObjectLifetime.AppDomain:
dict = RegisteredTaskObjectCacheBase.s_appDomainLifetimeObjects;
break;
}
return dict;
}
/// <summary>
/// Cleans up a cache collection.
/// </summary>
private static void DisposeObjects(Lazy<ConcurrentDictionary<object, object>> lifetimeObjects)
{
if (lifetimeObjects.IsValueCreated)
{
foreach (var obj in lifetimeObjects.Value.Values)
{
try
{
IDisposable disposable = obj as IDisposable;
disposable?.Dispose();
}
catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex))
{
// Eat it. We don't have a way to log here because at a minimum the build has already completed.
}
}
lifetimeObjects.Value.Clear();
}
}
}
}
|