// 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); } } } }