////////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   KernelSpinlock.cs
//
//  Note:
//      Kernel spinlock functionality
//
using System;
using Microsoft.Singularity;
using Microsoft.Singularity.Audit;
using System.Runtime.CompilerServices;
using System.Threading;

namespace System.Threading
{
    ///
    /// <summary>
    ///     Implementation of Kernel Spinlocks
    /// </summary>
    ///
    [NoCCtor]
    [CLSCompliant(false)]
    [AccessedByRuntime("referenced from halidt.asm")]
    public struct SpinLock
    {
        ///
        /// <summary>
        ///     Spinlock Rank is enumerator used to enumerate spinlock  ranks. The rule is:
        ///     holders of lower rank spinlocks can't acquire spinlocks with higher ranks.
        ///     Note this needs to be a bitmask enum because of the validation logic in Thread.cs
        /// </summary>
        ///
        internal enum Ranks : short
        {
            /// <summary> Placeholder. eventually all spinlocks should be ranked </summary>
            NoRank                  = 0x0,

            /// <summary>
            ///     Used by flag pages (low level memory management). This is nested
            ///     within several spin locks with Dispatcher rank, including
            ///     PageManager (GC) and linked stack.
            /// </summary>
            FlatPages               = 0x1,

            /// <summary>
            ///     Use by Hal. This can be nested within other
            ///     spin locks with Dispatcher rank.
            /// </summary>
            Hal                     = 0x2,

            /// <summary>
            ///     Used by scheduler and dispatcher code, and other code run with
            ///     interrupt disabled.
            /// </summary>
            Dispatcher              = 0x4,

            /// <summary>
            ///     The lowest rank above Dispatcher. A thread holding a lock with this rank
            ///     cannot acquire another spinlock, unless it enters scheduling code through
            ///     Yield or blocking. Most code above dispatcher level should use this rank.
            ///
            ///     Add higher ranks only if the code needs to acquire multiple spinlocks,
            ///     which should be avoided unless absolutely necessary.
            /// </summary>
            Passive                 = 0x8,

            /// <summary>
            ///     Used by services implemented in kernel
            /// </summary>
            Service                 = 0x10,
        };

        ///
        /// <summary>
        ///     Spinlock Type is enumerator used to enumerate spinlock types so that we can keep
        /// proper statistic for each spinlock. Statistic will allow us to identify spinlock problems
        /// </summary>
        ///
        public enum Types : int
        {
            NoType                      = 0,
            InterruptMutex              = (Ranks.Dispatcher         << RankShift) | 1,
            InterruptAutoResetEvent     = (Ranks.Dispatcher         << RankShift) | 2,
            AutoResetEvent              = (Ranks.Passive            << RankShift) | 3,
            Mutex                       = (Ranks.Passive            << RankShift) | 4,
            ManualResetEvent            = (Ranks.Passive            << RankShift) | 5,
            Timer                       = (Ranks.Hal                << RankShift) | 6,
            IoApic                      = (Ranks.Hal                << RankShift) | 7,
            MpHalClock                  = (Ranks.Hal                << RankShift) | 8,
            RTClock                     = (Ranks.Hal                << RankShift) | 9,
            HalScreen                   = (Ranks.Hal                << RankShift) | 10,
            FlatPages                   = (Ranks.FlatPages          << RankShift) | 11,
            VirtualMemoryRange          = (Ranks.Dispatcher         << RankShift) | 12,
            VMManager                   = (Ranks.Dispatcher         << RankShift) | 13,
            VMKernelMapping             = (Ranks.Dispatcher         << RankShift) | 14,
            ProtectionDomainTable       = (Ranks.Dispatcher         << RankShift) | 15,
            ProtectionDomainInit        = (Ranks.Dispatcher         << RankShift) | 16,
            ProtectionDomainMapping     = (Ranks.Dispatcher         << RankShift) | 17,
            SharedHeapAllocationOwner   = (Ranks.Service            << RankShift) | 18,
            PhysicalHeap                = (Ranks.Dispatcher         << RankShift) | 19,
            PhysicalPages               = (Ranks.Dispatcher         << RankShift) | 20,
            PageManager                 = (Ranks.Dispatcher         << RankShift) | 21,
            IoResources                 = (Ranks.Dispatcher         << RankShift) | 22,
            Finalizer                   = (Ranks.Dispatcher         << RankShift) | 23,
            MpExecutionFreeze           = (Ranks.Dispatcher         << RankShift) | 24,
            MpExecutionMpCall           = (Ranks.Dispatcher         << RankShift) | 25,
            Scheduler                   = (Ranks.Dispatcher         << RankShift) | 26,
            GCTracing                   = (Ranks.Dispatcher         << RankShift) | 27,
            IoIrq                       = (Ranks.Dispatcher         << RankShift) | 28,
            ServiceQueue                = (Ranks.Dispatcher         << RankShift) | 29,
            EndpointCore                = (Ranks.Dispatcher         << RankShift) | 30,
            KernelTestCase              = (Ranks.Dispatcher         << RankShift) | 31,
            HandleTable                 = (Ranks.Passive            << RankShift) | 32,
            ThreadTable                 = (Ranks.Passive            << RankShift) | 33,
            ProcessTable                = (Ranks.Passive            << RankShift) | 34,
            Thread                      = (Ranks.Passive            << RankShift) | 35,
            MaxTypeId                   = (Thread & TypeMask) +1
        };

        ///
        /// <summary>
        ///    Static initializer
        /// </summary>
        ///
        static public void StaticInitialize()
        {
        }

        ///
        /// <summary>
        ///     Init spinlock
        /// </summary>
        /// <param name="type">Type of SpinLock</param>
        ///
        public SpinLock(Types type)
        {
            baseLock = new SpinLockBase((int)type);
        }

        ///
        /// <summary>
        ///     SpinLock Rank
        /// </summary>
        ///
        internal int Rank
        {
            [NoHeapAllocation]
            get
            {
                return baseLock.Type >> RankShift;
            }
        }

        ///
        /// <summary>
        ///     SpinLock Type
        /// </summary>
        ///
        internal int Type
        {
            [NoHeapAllocation]
            get
            {
                return baseLock.Type & TypeMask;
            }
        }

        ///
        /// <summary>
        ///     Acquire spinlock
        /// </summary>
        ///
        [NoHeapAllocation]
        [Inline]
        public void Acquire()
        {
            Thread  thread = Thread.CurrentThread;
            int     threadId = (thread==null)?InitialThreadId:thread.GetThreadId();

            AcquireInternal(thread, threadId);
        }

        ///
        /// <summary>
        ///     Acquire spinlock
        /// </summary>
        ///
        /// <param name="threadId">Thread's Id acquiring spinlock</param>
        ///
        [NoHeapAllocation]
        [Inline]
        public void Acquire(int threadId)
        {
            AcquireInternal(Thread.GetThreadFromThreadId(threadId), threadId);
        }

        ///
        /// <summary>
        ///     Acquire spinlock
        /// </summary>
        ///
        /// <param name="thread">Thread acquiring spinlock</param>
        ///
        [NoHeapAllocation]
        [Inline]
        public void Acquire(Thread thread)
        {
            int threadId = (thread == null)?InitialThreadId:thread.GetThreadId();

            AcquireInternal(thread, threadId);
        }

        ///
        /// <summary>
        ///     Release spinlock
        /// </summary>
        ///
        [NoHeapAllocation]
        [Inline]
        public void Release()
        {
            Thread  thread = Thread.CurrentThread;
            int     threadId = (thread == null)?InitialThreadId:
                                            thread.GetThreadId();
            // Release spinlock
            ReleaseInternal(thread, threadId);
        }

        ///
        /// <summary>
        ///     Release spinlock
        /// </summary>
        ///
        /// <param name="threadId">Thread's Id releasing spinlock</param>
        ///
        [NoHeapAllocation]
        [Inline]
        public void Release(int threadId)
        {
            // Release spinlock
            ReleaseInternal(Thread.GetThreadFromThreadId(threadId), threadId);
        }

        ///
        /// <summary>
        ///     Release spinlock
        /// </summary>
        ///
        /// <param name="thread">Thread releasing spinlock</param>
        ///
        [NoHeapAllocation]
        [Inline]
        public void Release(Thread thread)
        {
            int threadId = (thread == null)?InitialThreadId:thread.GetThreadId();

            // Release spinlock
            ReleaseInternal(thread, threadId);
        }

        ///
        /// <summary>
        ///     Try to acquire the spin lock. Always return immediately.
        /// </summary>
        /// <returns> true if the spin lock is acquired. </returns>
        ///
        [NoHeapAllocation]
        [Inline]
        public bool TryAcquire()
        {
            Thread  thread = Thread.CurrentThread;
            int     threadId =(thread == null)?InitialThreadId:thread.GetThreadId();

            return TryAcquireInternal(thread, threadId);
        }

        ///
        /// <summary>
        ///     Try to acquire the spin lock. Always return immediately.
        /// </summary>
        ///
        /// <returns> true if the spin lock is acquired. </returns>
        ///
        /// <param name="thread">Thread acquiring spinlock</param>
        ///
        [NoHeapAllocation]
        [Inline]
        public bool TryAcquire(Thread thread)
        {
            int threadId = (thread == null)?InitialThreadId:thread.GetThreadId();

            return TryAcquireInternal(thread, thread.GetThreadId());
        }

        ///
        /// <summary>
        ///     Method to find out if spinlock is held by specified thread
        /// </summary>
        /// <returns> true if the spin lock is acquired. </returns>
        ///
        /// <param name="thread">Thread to verify possible spinlock's ownership</param>
        ///
        [NoHeapAllocation]
        public bool IsHeldBy(Thread thread)
        {
            int threadId = (thread == null)?InitialThreadId:thread.GetThreadId();
            return baseLock.IsHeldBy(threadId + 1);
        }

        ///
        /// <summary>
        ///     Method to find out if spinlock is held by specified thread
        /// </summary>
        /// <returns> true if the spin lock is acquired. </returns>
        ///
        /// <param name="threadId">Thread's Id to verify possible spinlock's ownership</param>
        ///
        [NoHeapAllocation]
        public bool IsHeldBy(int threadId)
        {
            return baseLock.IsHeldBy(threadId + 1);
        }

        ///
        /// <summary>
        ///     Assert thatf spinlock is held by specified thread
        /// </summary>
        ///
        /// <param name="thread">Thread to verify possible spinlock's ownership</param>
        ///
        [System.Diagnostics.Conditional("DEBUG")]
        [NoHeapAllocation]
        public void AssertHeldBy(Thread thread)
        {
            VTable.Assert(IsHeldBy(thread));
        }

        ///
        /// <summary>
        ///     Assert thatf spinlock is held by specified thread
        /// </summary>
        ///
        /// <param name="threadId">Thread's Id to verify possible spinlock's ownership</param>
        ///
        [System.Diagnostics.Conditional("DEBUG")]
        [NoHeapAllocation]
        public void AssertHeldBy(int threadId)
        {
            VTable.Assert(IsHeldBy(threadId));
        }

        ///
        /// <summary>
        ///     Given integer : derive type of a lock
        /// </summary>
        ///
        /// <param name="type">Parameter from which we can derive actual type of spinlock</param>
        ///
        [NoHeapAllocation]
        [Inline]
        internal static int DeriveType (int type)
        {
            // Type has to be in correct range
            VTable.Assert((type & TypeMask) >= (int)Types.NoType &&
                        (type & TypeMask) < (int)Types.MaxTypeId);

            return (type & (int)TypeMask);
        }

        ///
        /// <summary>
        ///     Given integer : derive type of a lock
        /// </summary>
        ///
        /// <param name="type">Parameter from which we can derive actual rank of spinlock</param>
        ///
        [NoHeapAllocation]
        [Inline]
        internal static int DeriveRank (int type)
        {
            // Type has to be in correct range
            VTable.Assert((type & TypeMask) >= (int)Types.NoType &&
                        (type & TypeMask) < (int)Types.MaxTypeId);

            return (type >> RankShift);
        }

        ///
        /// <summary>
        ///     Try to acquire the spin lock. Always return immediately.
        /// </summary>
        /// <returns> true if the spin lock is acquired. </returns>
        ///
        /// <param name="thread">Thread acquiring spinlock</param>
        /// <param name="threadId">Thread's Id acquiring spinlock</param>
        ///
        [NoHeapAllocation]
        [Inline]
        private bool TryAcquireInternal(Thread thread, int threadId)
        {
            bool result;

            // Assert preconditions:for spinlock with a rank = DisabledInterrupts and below
            // interrupts have to be disabled.
            VTable.Assert(Rank == (int)Ranks.NoRank ||
                        Rank > (int)Ranks.Dispatcher ||
                        Processor.InterruptsDisabled());


            // Notify thread that we are about to acquire spinlock
            if (thread != null) {
                thread.NotifySpinLockAboutToAcquire(this.baseLock.Type);
            }

            result = baseLock.TryAcquire(threadId + 1);

            // If we didn't acquire spinlock -we should notify thread about it: Just use release
            // notification
            if (thread != null && !result) {
                thread.NotifySpinLockReleased(this.baseLock.Type);
            }

            return result;
        }

        ///
        /// <summary>
        ///     Acquire the spin lock.
        /// </summary>
        ///
        /// <param name="thread">Thread acquiring spinlock</param>
        /// <param name="threadId">Thread's Id acquiring spinlock</param>
        ///
        [NoHeapAllocation]
        [Inline]
        private void AcquireInternal(Thread thread, int threadId)
        {
            // Assert preconditions:for spinlock with a rank = DisabledInterrupts and below
            // interrupts have to be disabled.
            VTable.Assert(Rank == (int)Ranks.NoRank ||
                        Rank > (int)Ranks.Dispatcher ||
                        Processor.InterruptsDisabled());


            // Thread has to be notified if we are about to acquire spinlock
            if (thread != null) {
                thread.NotifySpinLockAboutToAcquire(this.baseLock.Type);
            }

            // Get lock
            baseLock.Acquire(threadId + 1);
        }

        ///
        /// <summary>
        ///     Release the spin lock.
        /// </summary>
        ///
        /// <param name="thread">Thread releasing spinlock</param>
        /// <param name="threadId">Thread's Id releasing spinlock</param>
        ///
        [NoHeapAllocation]
        [Inline]
        private void ReleaseInternal(Thread thread, int threadId)
        {
            // Assert preconditions:for spinlock with a rank = DisabledInterrupts and below
            // interrupts have to be disabled.
            VTable.Assert(Rank == (int)Ranks.NoRank ||
                        Rank > (int)Ranks.Dispatcher ||
                        Processor.InterruptsDisabled());


            // Release spinlock
            baseLock.Release(threadId + 1);

            if (thread != null) {
                // Don't forget to notify thread that it just released spinlock
                thread.NotifySpinLockReleased(this.baseLock.Type);
            }
        }

        /// <summary> Constant defines shift of the rank</summary>
        internal const int                          RankShift = 0x10;

        /// <summary> Constant defines mask for getting type of gompound spinlock type </summary>
        internal const int                          TypeMask = 0xFFFF;

        /// <summary> Id of a initial thread </summary>
        private const int                           InitialThreadId = -2;

        /// <summary> Actual mechanism implementing spinlock</summary>
        private SpinLockBase                        baseLock;
    }

    /// <summary> Attribute to mark methods that stop lock rank verification </summary>
    [Layer(0)]
    public class IgnoreLockRankAttribute : Attribute
    {
        public IgnoreLockRankAttribute() {}
    }

    /// <summary> Attribute to mark classes / methods that hold FlatPages locks </summary>
    [Layer(1)]
    public class FlatPagesLockAttribute : Attribute
    {
        public FlatPagesLockAttribute() {}
    }

    /// <summary> Attribute to mark classes / methods that hold Hal locks </summary>
    [Layer(2)]
    public class HalLockAttribute : Attribute
    {
        public HalLockAttribute() {}
    }

    /// <summary> Attribute to mark classes / methods that hold Dispatcher locks </summary>
    [Layer(3)]
    public class DispatcherLockAttribute : Attribute
    {
        public DispatcherLockAttribute() {}
    }

    /// <summary> Attribute to mark classes / methods that hold Passive locks </summary>
    [Layer(4)]
    public class PassiveLockAttribute : Attribute
    {
        public PassiveLockAttribute() {}
    }

    /// <summary> Attribute to mark classes / methods that hold Service locks </summary>
    [Layer(5)]
    public class ServiceLockAttribute : Attribute
    {
        public ServiceLockAttribute() {}
    }
}
