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

Reduce allocations in ShellScope #15937

Merged
merged 1 commit into from
May 1, 2024
Merged
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
92 changes: 69 additions & 23 deletions src/OrchardCore/OrchardCore.Abstractions/Shell/Scope/ShellScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ public sealed class ShellScope : IServiceScope, IAsyncDisposable
private static readonly AsyncLocal<ShellScopeHolder> _current = new();

private readonly AsyncServiceScope _serviceScope;
private readonly Dictionary<object, object> _items = [];
private readonly List<Func<ShellScope, Task>> _beforeDispose = [];
private readonly HashSet<string> _deferredSignals = [];
private readonly List<Func<ShellScope, Task>> _deferredTasks = [];
private readonly List<Func<ShellScope, Exception, Task>> _exceptionHandlers = [];
private Dictionary<object, object> _items;
private List<Func<ShellScope, Task>> _beforeDispose;
private HashSet<string> _deferredSignals;
private List<Func<ShellScope, Task>> _deferredTasks;
private List<Func<ShellScope, Exception, Task>> _exceptionHandlers;

private bool _serviceScopeOnly;
private bool _shellTerminated;
Expand Down Expand Up @@ -84,35 +84,68 @@ public ShellScope(ShellContext shellContext)
/// </summary>
public static void Set(object key, object value)
{
if (Current is not null)
var current = Current;

if (current != null)
{
Current._items[key] = value;
current._items ??= [];

current._items[key] = value;
}
}

/// <summary>
/// Gets a shared item from the current shell scope.
/// </summary>
public static object Get(object key) => Current is null ? null : Current._items.TryGetValue(key, out var value) ? value : null;
public static object Get(object key)
{
var current = Current;

if (current == null)
{
return null;
}

current._items ??= [];

return current._items.TryGetValue(key, out var value) ? value : null;
}

/// <summary>
/// Gets a shared item of a given type from the current shell scope.
/// </summary>
public static T Get<T>(object key) => Current is null ? default : Current._items.TryGetValue(key, out var value) ? value is T item ? item : default : default;
public static T Get<T>(object key)
{
var current = Current;

if (current == null)
{
return default;
}

current._items ??= [];

return current._items.TryGetValue(key, out var value) ? value is T item ? item : default : default;
}

/// <summary>
/// Gets (or creates) a shared item of a given type from the current shell scope.
/// </summary>
public static T GetOrCreate<T>(object key, Func<T> factory)
{
if (Current is null)
var current = Current;

if (current == null)
{
return factory();
}

if (!Current._items.TryGetValue(key, out var value) || value is not T item)
current._items ??= [];


if (!current._items.TryGetValue(key, out var value) || value is not T item)
{
Current._items[key] = item = factory();
current._items[key] = item = factory();
}

return item;
Expand All @@ -123,14 +156,19 @@ public static T GetOrCreate<T>(object key, Func<T> factory)
/// </summary>
public static T GetOrCreate<T>(object key) where T : class, new()
{
if (Current is null)
var current = Current;

if (current == null)
{
return new T();
}

if (!Current._items.TryGetValue(key, out var value) || value is not T item)
current._items ??= [];


if (!current._items.TryGetValue(key, out var value) || value is not T item)
{
Current._items[key] = item = new T();
current._items[key] = item = new T();
}

return item;
Expand Down Expand Up @@ -338,22 +376,22 @@ internal async Task ActivateShellInternalAsync()
/// <summary>
/// Registers a delegate to be invoked when 'BeforeDisposeAsync()' is called on this scope.
/// </summary>
internal void BeforeDispose(Func<ShellScope, Task> callback) => _beforeDispose.Insert(0, callback);
internal void BeforeDispose(Func<ShellScope, Task> callback) => (_beforeDispose ??= []).Insert(0, callback);

/// <summary>
/// Adds a Signal (if not already present) to be sent just after 'BeforeDisposeAsync()'.
/// </summary>
internal void DeferredSignal(string key) => _deferredSignals.Add(key);
internal void DeferredSignal(string key) => (_deferredSignals ??= []).Add(key);

/// <summary>
/// Adds a Task to be executed in a new scope after 'BeforeDisposeAsync()'.
/// </summary>
internal void DeferredTask(Func<ShellScope, Task> task) => _deferredTasks.Add(task);
internal void DeferredTask(Func<ShellScope, Task> task) => (_deferredTasks ??= []).Add(task);

/// <summary>
/// Adds an handler to be invoked if an exception is thrown while executing in this shell scope.
/// </summary>
internal void ExceptionHandler(Func<ShellScope, Exception, Task> callback) => _exceptionHandlers.Add(callback);
internal void ExceptionHandler(Func<ShellScope, Exception, Task> callback) => (_exceptionHandlers ??= []).Add(callback);

/// <summary>
/// Registers a delegate to be invoked before the current shell scope will be disposed.
Expand All @@ -380,6 +418,11 @@ internal async Task ActivateShellInternalAsync()
/// </summary>
public async Task HandleExceptionAsync(Exception e)
{
if (_exceptionHandlers == null)
{
return;
}

foreach (var callback in _exceptionHandlers)
{
await callback(this, e);
Expand All @@ -392,17 +435,20 @@ public async Task HandleExceptionAsync(Exception e)
/// </summary>
internal async Task BeforeDisposeAsync()
{
foreach (var callback in _beforeDispose)
if (_beforeDispose != null)
{
await callback(this);
foreach (var callback in _beforeDispose)
{
await callback(this);
}
}

if (_serviceScopeOnly)
{
return;
}

if (_deferredSignals.Count > 0)
if (_deferredSignals?.Count > 0)
{
var signal = ShellContext.ServiceProvider.GetRequiredService<ISignal>();
foreach (var key in _deferredSignals)
Expand All @@ -411,7 +457,7 @@ internal async Task BeforeDisposeAsync()
}
}

if (_deferredTasks.Count > 0)
if (_deferredTasks?.Count > 0)
{
var shellHost = ShellContext.ServiceProvider.GetRequiredService<IShellHost>();

Expand Down