////////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   ThreadContext.cs
//
//  Note:
//

namespace Microsoft.Singularity
{
    using System;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Threading;

    using Microsoft.Singularity.V1.Threads;
    using Microsoft.Singularity.Isal;

    [NoCCtor]
    [CLSCompliant(false)]
    [StructLayout(LayoutKind.Sequential)]
    [StructAlign(16)]
    [AccessedByRuntime("referenced from c++")]
    internal struct ThreadContext
    {
        [AccessedByRuntime("referenced from c++")]
        internal ThreadRecord   threadRecord;

        [AccessedByRuntime("referenced from c++")]
        internal ushort num;
        [AccessedByRuntime("referenced from c++")]
        internal ushort regs;
        [AccessedByRuntime("referenced from c++")]
        internal unsafe ThreadContext * prev;
        [AccessedByRuntime("referenced from c++")]
        internal unsafe ThreadContext * next;

        [AccessedByRuntime("referenced from c++")]
        internal unsafe ThreadContext * prevInKern;
        [AccessedByRuntime("referenced from c++")]
        internal unsafe ThreadContext * nextInKern;
        [AccessedByRuntime("referenced from c++")]
        internal unsafe ThreadContext * prevInProc;
        [AccessedByRuntime("referenced from c++")]
        internal unsafe ThreadContext * nextInProc;

        [AccessedByRuntime("referenced from c++")]
        internal UIntPtr stackBegin;
        [AccessedByRuntime("referenced from c++")]
        internal UIntPtr stackLimit;
        [AccessedByRuntime("referenced from c++")]
        internal ushort processId;
        [AccessedByRuntime("referenced from c++")]
        internal bool uncaughtFlag; // true means "uncaught exception from process, so throw exception in kernel"
        [AccessedByRuntime("referenced from halforgc.asm")]
        internal bool suspendAlert; // true means "check whether thread should suspend"

        // Note: These two forks must be exactly the same size, but their
        // fields alias differently for kernel or process code.
#if SINGULARITY_KERNEL
        [AccessedByRuntime("referenced from c++")]
        internal unsafe void *_thread;
        [AccessedByRuntime("referenced from c++")]
        internal UIntPtr processThread;
        [AccessedByRuntime("referenced from c++")]
        internal unsafe System.GCs.CallStack.TransitionRecord * stackMarkers;
        [AccessedByRuntime("referenced from c++")]
        internal unsafe System.GCs.CallStack.TransitionRecord * processMarkers;
        [AccessedByRuntime("referenced from c++")]
        internal ushort threadIndex;
        [AccessedByRuntime("referenced from c++")]
        internal ushort processThreadIndex;
#elif SINGULARITY_PROCESS
        [AccessedByRuntime("referenced from c++")]
        internal UIntPtr kernelThread;
        [AccessedByRuntime("referenced from c++")]
        internal unsafe void *_thread;
        [AccessedByRuntime("referenced from c++")]
        internal unsafe System.GCs.CallStack.TransitionRecord * kernelMarkers;
        [AccessedByRuntime("referenced from c++")]
        internal unsafe System.GCs.CallStack.TransitionRecord * stackMarkers;
        [AccessedByRuntime("referenced from c++")]
        internal ushort kernelThreadIndex;
        [AccessedByRuntime("referenced from c++")]
        internal ushort threadIndex;
#endif
        [AccessedByRuntime("referenced from halforgc.asm")]
        internal unsafe int gcStates;
        // See ThreadContext.IsInKernelMode
        private unsafe System.GCs.CallStack.TransitionRecord * modeMarker;

#if PAGING
        [AccessedByRuntime("referenced from c++")]
        internal UIntPtr abiStackHead;
        [AccessedByRuntime("referenced from c++")]
        internal UIntPtr abiStackBegin;
        [AccessedByRuntime("referenced from c++")]
        internal UIntPtr abiStackLimit;
#endif

#if THREAD_TIME_ACCOUNTING
        [AccessedByRuntime("referenced from c++")]
        internal ulong lastExecutionTimeUpdate;

        [AccessedByRuntime("referenced from c++")]
        internal ulong executionTime;
#endif
        //////////////////////////////////////////////// Methods & Properties.
        //
        internal Thread thread {
            [NoHeapAllocation]
            get { return GetThread(); }
        }

        [NoHeapAllocation]
        internal unsafe bool IsFirst()
        {
            return (((UIntPtr)prev) == UIntPtr.Zero);
        }

        [AccessedByRuntime("referenced from c++")]
        // Return true if the thread is in kernel mode, false if the
        // thread is in process mode.
        // Note that by the time this method returns, the thread might
        // have already switched to a different mode; in other words,
        // don't rely on this result of this method being up-to-date unless
        // the thread is suspended or blocked.
        [NoHeapAllocation]
        internal unsafe bool IsInKernelMode()
        {
            // When a thread enters process mode, RuntimeEntryPoint
            // sets modeMarker=processMarkers.  Thus, in process mode:
            //
            //   modeMarker == processMarkers
            //
            // When a thread in process mode calls the kernel, pushStackMark
            // pushes a child process marker onto the stack, so that
            // modeMarker is left pointing to the new processMarker's
            // parent (the old processMarker).  Thus, in kernel mode:
            //
            //   modeMarker == parent(processMarkers)
            //
            // This keeps the mode in sync with the process stack markers,
            // which is convenient though not essential.  This assumes that
            // the only reason a process pushes a stack marker is to enter
            // kernel mode.
            //
            // Since processMarkers may be null, ThreadContext.ParentModeMarker
            // defines a special definition for parent(null).
#if SINGULARITY_PROCESS
            System.GCs.CallStack.TransitionRecord * processMarkers =
                stackMarkers;
#endif // SINGULARITY_PROCESS
            if (modeMarker == processMarkers) {
                return false;
            }
            else {
                VTable.Assert(modeMarker == ParentModeMarker(processMarkers));
                return true;
            }
        }

        [AccessedByRuntime("referenced from c++")]
        [NoHeapAllocation]
        internal unsafe void SetKernelMode()
        {
#if SINGULARITY_PROCESS
            System.GCs.CallStack.TransitionRecord * processMarkers =
                stackMarkers;
#endif // SINGULARITY_PROCESS
            modeMarker = ParentModeMarker(processMarkers);
        }

        [AccessedByRuntime("referenced from c++")]
        [NoHeapAllocation]
        internal unsafe void SetProcessMode()
        {
#if SINGULARITY_PROCESS
            System.GCs.CallStack.TransitionRecord * processMarkers =
                stackMarkers;
#endif // SINGULARITY_PROCESS
            modeMarker = processMarkers;
        }

        [NoHeapAllocation]
        internal bool IsRunning()
        {
            return !threadRecord.spill.IsSpilled;
        }

        [NoHeapAllocation]
        static private unsafe System.GCs.CallStack.TransitionRecord *
            ParentModeMarker(System.GCs.CallStack.TransitionRecord * child)
        {
            System.GCs.CallStack.TransitionRecord * bottom =
                (System.GCs.CallStack.TransitionRecord *) (-1);

            VTable.Assert(child != bottom);
            if (child == null) {
                return bottom;
            }
            else {
                return child->oldTransitionRecord;
            }
        }

        //////////////////////////////////////////////////// External Methods.
        //
        [AccessedByRuntime("output to header: defined in c++")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(32)]
        [NoHeapAllocation]
        private extern Thread GetThread();

        [AccessedByRuntime("output to header: defined in c++")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [StackBound(32)]
        [NoHeapAllocation]
        internal extern void UpdateAfterGC(Thread thread);

#if SINGULARITY_KERNEL
        [AccessedByRuntime("output to header: defined in c++")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [GCAnnotation(GCOption.NOGC)]
        [StackBound(32)]
        [NoHeapAllocation]
        internal extern void Initialize(int threadIndex, UIntPtr stackBegin, uint cr3);

        [AccessedByRuntime("output to header: defined in c++")]
        [MethodImpl(MethodImplOptions.InternalCall)]
        [GCAnnotation(GCOption.NOGC)]
        [StackBound(32)]
        [NoHeapAllocation]
        internal extern void InitializeIdle(int threadIndex, UIntPtr stackBegin, uint cr3);
#endif
    }
}

// This is a temporary workaround to get around bartok assumptions
namespace Microsoft.Singularity.X86
{
  using System.Runtime.CompilerServices;

  struct ProcessorContext
  {
      [RequiredByBartok]
      Microsoft.Singularity.X86.ThreadContext threadContext;
  }

  struct ThreadContext
  {
      [RequiredByBartok]
      System.UIntPtr stackLimit;
  }
}
