| File: Language\Intermediate\LazyContent.cs | Web Access |
| Project: src\roslyn\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler) |
// 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.Razor.Language.Intermediate; internal abstract class LazyContent { private LazyContent() { } public abstract string Value { get; } public static LazyContent Create<T>(T arg, Func<T, string> contentFactory) { ArgHelper.ThrowIfNull(contentFactory); return new LazyContentImpl<T>(arg, contentFactory); } private sealed class LazyContentImpl<T>(T arg, Func<T, string> contentFactory) : LazyContent { private const int Uninitialized = 0; private const int Computing = 1; private const int Initialized = 2; private T _arg = arg; private Func<T, string?> _contentFactory = contentFactory; private string _value = null!; private int _state; public override string Value { get { // Return the value if it has already been initialized; otherwise, compute it. return Volatile.Read(ref _state) == Initialized ? _value : GetOrComputeAndStoreValue(); } } private string GetOrComputeAndStoreValue() { SpinWait spinner = default; while (true) { switch (Interlocked.CompareExchange(ref _state, Computing, Uninitialized)) { case Uninitialized: Debug.Assert(_contentFactory is not null, "Content factory should not be null at this point."); // This thread gets to compute the value and clear the references for GC. _value = _contentFactory(_arg) ?? string.Empty; _arg = default!; _contentFactory = null!; Volatile.Write(ref _state, Initialized); return _value; case Computing: // Another thread is already computing the value, wait for it to finish. spinner.SpinOnce(); continue; case Initialized: // The value has been initialized by another thread. Return it! return _value; default: return Assumed.Unreachable<string>(); } } } } }