// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
using System.Runtime.CompilerServices;
namespace Stride.Core.MicroThreading;
///
/// Provides microthread-local storage of data.
///
/// Type of data stored.
public class MicroThreadLocal where T : class
{
private readonly Func? valueFactory;
private readonly ConditionalWeakTable values = [];
///
/// The value return when we are not in a micro thread. That is the value return when 'Scheduler.CurrentMicroThread==null'
///
private T? valueOutOfMicrothread;
///
/// Indicate if the value out of micro-thread have been set at least once or not.
///
private bool valueOutOfMicrothreadSet;
///
/// Initializes a new instance of the class.
///
public MicroThreadLocal()
: this(null)
{
}
///
/// Initializes a new instance of the class.
///
/// The value factory invoked to create a value when is retrieved before having been previously initialized.
public MicroThreadLocal(Func? valueFactory)
{
this.valueFactory = valueFactory;
}
///
/// Gets or sets the value for the current microthread.
///
///
/// The value for the current microthread.
///
public T? Value
{
get
{
T? value;
var microThread = Scheduler.CurrentMicroThread;
lock (values)
{
if (microThread == null)
{
if (!valueOutOfMicrothreadSet)
valueOutOfMicrothread = valueFactory?.Invoke();
value = valueOutOfMicrothread;
}
else if (!values.TryGetValue(microThread, out value))
{
values.Add(microThread, value = valueFactory?.Invoke());
}
}
return value;
}
set
{
var microThread = Scheduler.CurrentMicroThread;
lock (values)
{
if (microThread == null)
{
valueOutOfMicrothread = value;
valueOutOfMicrothreadSet = true;
}
else
{
values.Remove(microThread);
values.Add(microThread, value);
}
}
}
}
public bool IsValueCreated
{
get
{
var microThread = Scheduler.CurrentMicroThread;
lock (values)
{
if (microThread == null)
return valueOutOfMicrothreadSet;
return values.TryGetValue(microThread, out var value);
}
}
}
public void ClearValue()
{
var microThread = Scheduler.CurrentMicroThread;
lock (values)
{
if (microThread == null)
{
valueOutOfMicrothread = default;
valueOutOfMicrothreadSet = false;
}
else
{
values.Remove(microThread);
}
}
}
}