// 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; 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)] public 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. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lock Enter(ref this SpinLock spinLock) { return new(ref spinLock); } #else /// /// Enters a specified instance and returns a wrapper to use to release the lock. /// This extension should be used though a block or statement: /// /// private SpinLock spinLock = new SpinLock(); /// /// public void Foo() /// { /// using (SpinLockExtensions.Enter(this, ref spinLock)) /// { /// // Thread-safe code here... /// } /// } /// /// The compiler will take care of releasing the SpinLock when the code goes out of that scope. /// /// The owner to create a portable reference for. /// The target to use (it must be within ). /// 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 Lock Enter(object owner, ref SpinLock spinLock) { return new(owner, ref spinLock); } #endif /// /// A that is used to enter and hold a through a block or statement. /// [EditorBrowsable(EditorBrowsableState.Never)] 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; #if NETSTANDARD2_1_OR_GREATER /// /// Initializes a new instance of the struct. /// /// The target to use. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lock(ref SpinLock spinLock) { this.spinLock = new Ref(ref spinLock); this.lockTaken = false; spinLock.Enter(ref this.lockTaken); } #else /// /// Initializes a new instance of the struct. /// /// The owner to create a portable reference for. /// The target to use (it must be within ). [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lock(object owner, ref SpinLock spinLock) { this.spinLock = new Ref(owner, ref spinLock); this.lockTaken = false; spinLock.Enter(ref this.lockTaken); } #endif /// /// Implements the duck-typed method and releases the current instance. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { if (this.lockTaken) { this.spinLock.Value.Exit(); } } } }