// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.ComponentModel; using System.Runtime.CompilerServices; #if NETSTANDARD2_1_OR_GREATER using System.Runtime.Versioning; #endif using System.Threading; namespace CommunityToolkit.HighPerformance; /// /// Helpers for working with the type. /// public static class SpinLockExtensions { /// /// Enters a specified instance and returns a wrapper to use to release the lock. /// This extension should be used though a block or statement: /// /// SpinLock spinLock = new SpinLock(); /// /// using (SpinLockExtensions.Enter(&spinLock)) /// { /// // Thread-safe code here... /// } /// /// The compiler will take care of releasing the SpinLock when the code goes out of that scope. /// /// A pointer to the target to use /// A wrapper type that will release when its method is called. /// The returned value shouldn't be used directly: use this extension in a block or statement. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe UnsafeLock Enter(SpinLock* spinLock) { return new(spinLock); } /// /// A that is used to enter and hold a through a block or statement. /// [EditorBrowsable(EditorBrowsableState.Never)] public readonly unsafe ref struct UnsafeLock { /// /// The * pointer to the target value to use. /// private readonly SpinLock* spinLock; /// /// A value indicating whether or not the lock is taken by this instance. /// private readonly bool lockTaken; /// /// Initializes a new instance of the struct. /// /// The target to use. [MethodImpl(MethodImplOptions.AggressiveInlining)] internal UnsafeLock(SpinLock* spinLock) { this.spinLock = spinLock; this.lockTaken = false; spinLock->Enter(ref this.lockTaken); } /// /// Implements the duck-typed method and releases the current instance. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { if (this.lockTaken) { this.spinLock->Exit(); } } } #if NETSTANDARD2_1_OR_GREATER /// /// Enters a specified instance and returns a wrapper to use to release the lock. /// This extension should be used though a block or statement: /// /// SpinLock spinLock = new SpinLock(); /// /// using (spinLock.Enter()) /// { /// // Thread-safe code here... /// } /// /// The compiler will take care of releasing the SpinLock when the code goes out of that scope. /// /// The target to use /// A wrapper type that will release when its method is called. /// The returned value shouldn't be used directly: use this extension in a block or statement. [RequiresPreviewFeatures( "The Lock type has no compiler support to ensure the lifetime of referenced values is respected, and as such using it incorrectly may lead to GC holes.", Url = "https://github.com/dotnet/runtime/issues/46104")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lock Enter(ref this SpinLock spinLock) { return new(ref spinLock); } /// /// A that is used to enter and hold a through a block or statement. /// [EditorBrowsable(EditorBrowsableState.Never)] [RequiresPreviewFeatures( "The Lock type has no compiler support to ensure the lifetime of referenced values is respected, and as such using it incorrectly may lead to GC holes.", Url = "https://github.com/dotnet/runtime/issues/46104")] public readonly ref struct Lock { /// /// The instance pointing to the target value to use. /// private readonly Ref spinLock; /// /// A value indicating whether or not the lock is taken by this instance. /// private readonly bool lockTaken; /// /// Initializes a new instance of the struct. /// /// The target to use. [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Lock(ref SpinLock spinLock) { this.spinLock = new Ref(ref spinLock); this.lockTaken = false; spinLock.Enter(ref this.lockTaken); } /// /// Implements the duck-typed method and releases the current instance. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { if (this.lockTaken) { this.spinLock.Value.Exit(); } } } #endif }