// ==++==
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// UMSThreadInternalContext.h
//
// Header file containing the metaphor for a UMS thread based internal execution context/stack.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#pragma once

namespace Concurrency
{
namespace details
{

    class UMSThreadInternalContext : public InternalContextBase
    {
    public:
        //
        // Public Methods
        //

        /// <summary>
        ///     Construct an internal thread based context.
        /// </summary>
        UMSThreadInternalContext(SchedulerBase *pScheduler);

        /// <summary>
        ///     Destroys an internal thread based context.
        /// </summary>
        virtual ~UMSThreadInternalContext();

        /// <summary>
        ///     Causes the internal context to block yielding the virtual processor to a different internal context.
        /// </summary>
        virtual void Block();

        /// <summary>
        ///     Returns the type of context
        /// </summary>
        virtual ContextKind GetContextKind() const 
        { 
            return UMSThreadContext;
        }

        /// <summary>
        ///     Depending on the argument, causes the scheduler to add an extra virtual processor for the
        ///     duration of a block of code or remove a previously added one.
        ///
        ///         Oversubscribe(true);
        ///         // some slow kernel or I/O code, etc.
        ///         Oversubscribe(false);
        ///
        ///     UMS provides automatic oversubscription. This API is largely ignored in UMS except for enforcing
        ///     the matching Oversubscribe(false) API as per the spec.
        /// </summary>
        /// <param name="beginOversubscription">
        ///     A boolean value specifying whether oversubscription is to be turned on or off.
        /// </param>
        virtual void Oversubscribe(bool beginOversubscription);

        /// <summary>
        ///     The method that is called when a thread proxy starts executing a particular context.  The thread proxy which executes
        ///     the context is passed into this method and must be saved and returned on a call to the get_Proxy method.
        /// </summary>
        /// <param name="pDispatchState">
        ///     The state under which this IExecutionContext is being dispatched.
        /// </param>
        virtual void Dispatch(DispatchState * pDispatchState);

        /// <summary>
        ///     Enters a critical region of the scheduler.  Calling this guarantees that the virtual processor on which this context lives
        ///     is guaranteed to be stable throughout the critical region.  For some context types, this is virtually a NOP.  For others 
        ///     (UMS), this makes it appear that blocking on the context actually blocks the UMS thread instead of triggering return to 
        ///     primary.  Note that critical regions suppress asynchronous blocking but not synchronous blocking.
        /// </summary>
        virtual int EnterCriticalRegionHelper();

        /// <summary>
        ///     Exits a critical region of the scheduler.
        /// </summary>
        virtual int ExitCriticalRegionHelper();

        /// <summary>
        ///     Checks if a context is in a critical region.  This is only safe from either the current context or from a UMS primary which
        ///     has woken due to a given context blocking.
        /// </summary>
        virtual CriticalRegionType GetCriticalRegionType() const
        {
            CORE_ASSERT(m_pThreadProxy != NULL);
            return (static_cast<IUMSThreadProxy *>(m_pThreadProxy))->GetCriticalRegionType();
        }

        /// <summary>
        ///     Enters a hyper-critical region of the scheduler.  Calling this guarantees not only the conditions of a critical region but it
        ///     guarantees that synchronous blocking is suppressed as well.  This allows for lock sharing between the primary and hyper-critical
        ///     regions running on UTs.  No lock sharing can occur between the inside of this region type and the outside of this region type
        ///     on a UT.
        /// </summary>
        virtual int EnterHyperCriticalRegionHelper();

        /// <summary>
        ///     Exits a hyper-critical region of the scheduler.
        /// </summary>
        virtual int ExitHyperCriticalRegionHelper();

        /// <summary>
        ///     Determines whether or not the context is synchronously blocked at this given time.
        /// </summary>
        /// <returns>
        ///     Whether context is in synchronous block state.
        /// </returns>
        virtual bool IsSynchronouslyBlocked() const
        {
            return InternalContextBase::IsSynchronouslyBlocked() || ((m_blockedState & CONTEXT_UMS_SYNC_BLOCKED) != 0);
        }

    private:

        friend class UMSThreadScheduler;
        friend class UMSSchedulingContext;
        friend class UMSThreadVirtualProcessor;

        enum BlockingType
        {
            // No blocking registered.
            BlockingNone,

            // Normal blocking
            BlockingNormal,

            // Critical blocking (e.g.: Win32 blocking call inside a normal critical region)
            BlockingCritical
        };

        // The previous virtual processor we were running on.
        UMSThreadVirtualProcessor *m_pLastVirtualProcessor;

        // How we last blocked.
        volatile BlockingType m_blockingType; 

        /// <summary>
        ///     Sets the blocking type.
        /// </summary>
        void SetBlockingType(BlockingType blockingType)
        {
            m_blockingType = blockingType;
        }

        /// <summary>
        ///     Notifies the context that it's blocked.
        /// </summary>
        /// <param name="fAsynchronous">
        ///     Is previously running context asynchronously blocked.
        /// </param>
        void NotifyBlocked(bool fAsynchronous);

        /// <summary>
        ///     Spins until we've registered what blocking type the last block operation was and returns the type.
        /// </summary>
        BlockingType SpinOnAndReturnBlockingType();

        /// <summary>
        ///     Called before this executes on a given virtual processor.
        /// </summary>
        virtual void PrepareToRun(VirtualProcessor *pVProc);

    private:

        friend class UMSThreadScheduler;
        friend class UMSSchedulingContext;
        friend class UMSThreadVirtualProcessor;

        /// <summary>
        ///     Called when the RM wakes up the thread for some reason.
        /// </summary>
        virtual void RMAwaken()
        {
            CMTRACE(MTRACE_EVT_RMAWAKEN, this, m_pVirtualProcessor, NULL);

            //
            // *NOTE*: It is absolutely imperative that this bias awakening of virtual processors to the scheduling node of
            // m_pVirtualProcessor.  RMAwaken is called from an internal context when something comes back on the completion
            // list of a deactivated virtual processor.  Because things are added to runnables on an internal context, a cache
            // local schedule group may put items in the LRC.  The LRC is *NOT* checked when rambling from virtual processors
            // belonging to other scheduling nodes.  If we unblock a context and add it to the LRC on node A and then awake
            // a virtual processor on node B, the work will never be found and the scheduler will deadlock.  This is an artifact
            // of the way in which UMS completion list events come back on deactivated virtual processors.
            //
            (static_cast<UMSThreadScheduler *>(m_pScheduler))->MoveCompletionListToRunnables(m_pVirtualProcessor);
        }

        void * operator new(size_t _Size)
        {
            return ::operator new(_Size);
        }

        void operator delete(void * _Ptr)
        {
            ::operator delete(_Ptr);
        }
    };
} // namespace details
} // namespace Concurrency
