// ==++==
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// TransmogrifiedPrimary.h
//
// A very special primary thread whose sole purpose is to "virtually" transmogrify a UMS thread into an NT thread
// for the purposes of scheduler nesting.  Note that this class is also reused in any circumstance where similar behavior
// is required.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

namespace Concurrency
{
namespace details
{
    /// <summary>
    ///     A base interface for all UMS thread abstractions so that we could determine if the 
    ///     current thread is a primary or an UT.
    /// </summary>
    class UMSBaseObject
    {
    public:

        virtual bool IsPrimary() = 0;

    };

    class TransmogrifiedPrimary : public UMSBaseObject
    {
    public:

        /// <summary>
        ///     Creates a new primary which transmogrifies a series of proxies (via ExecuteToCompletion/QueueToCompletion) into "virtual threads" until
        ///     they exit -- one at a time.
        /// </summary>
        TransmogrifiedPrimary();

        /// <summary>
        ///     Destroys the transmogrified primary.
        /// </summary>
        ~TransmogrifiedPrimary();

        /// <summary>
        ///     Waits for queued completions.
        /// </summary>
        void WaitForCompletion()
        {
            WaitForSingleObject(m_hComplete, INFINITE);
        }

        /// <summary>
        ///     Queues a thread to execute to completion and asynchronously returns.  It is not safe to utilize this method and ExecuteToCompletion
        ///     simultaneously.
        /// </summary>
        /// <param name="pProxy">
        ///     The proxy to queue to completion.
        /// </param>
        void QueueToCompletion(UMSThreadProxy *pProxy);

        /// <summary>
        ///     Shuts down a multiple binding transmogrified primary.
        /// </summary>
        void Shutdown()
        {
            WaitForCompletion();
            m_fCanceled = true;
            Unblock();
        }

        /// <summary>
        ///     Indicates whether this is a primary or not.
        /// </summary>
        virtual bool IsPrimary()
        {
            return true;
        }

    protected:

        /// <summary>
        ///     Called when a transmogrification is complete.
        /// </summary>
        virtual void CompletedTransmogrification()
        {
        }

    private:
        
        // The UMS thread that this primary transmogrifies to a "virtual"-thread.
        UMSThreadProxy *m_pBoundProxy;

        // The primary thread.
        HANDLE m_hPrimary;

        // The block handle (we are signaled when the old primary indicates that transmogrification is complete)
        HANDLE m_hBlock;

        // The completion handle
        HANDLE m_hComplete;

        // The TID of the primary thread.
        DWORD m_primaryId;

        // The UMS completion list for this transmogrified primary.
        PUMS_COMPLETION_LIST m_pCompletionList;

        //
        // For multiple bindings.
        //
        SafeSQueue<UMSThreadProxy> m_queuedExecutions;
        volatile LONG m_queueCount;

        // Indication of canceled primary.
        bool m_fCanceled;

        /// <summary>
        ///     Initializes the class.
        /// </summary>
        void Initialize();

        /// <summary>
        ///     Unblocks the transmogrified primary so that it can execute the UT it is transmogrifying.
        /// </summary>
        void Unblock()
        {
            SetEvent(m_hBlock);
        }

        /// <summary>
        ///     Search for new work
        /// </summary>
        UMSThreadProxy * SearchForWork();

        /// <summary>
        ///     Handle blocking of a UT 
        /// </summary>
        UMSThreadProxy * HandleBlocking();

        /// <summary>
        ///     Handle yielding of a UT 
        /// </summary>
        UMSThreadProxy * HandleYielding();

        /// <summary>
        ///     Execute the given proxy on this primary
        /// </summary>
        /// <param name="pProxy">
        ///     The proxy to run
        /// </param>
        void Execute(UMSThreadProxy *pProxy);

        /// <summary>
        ///     Binds a new proxy to the transmogrified primary and returns it.
        /// </summary>
        UMSThreadProxy *BindNewProxy();

        /// <summary>
        ///     The UMS primary function.  This is invoked when the primary switches into UMS scheduling mode or whenever a given
        ///     context blocks or yields.
        /// </summary>
        /// <param name="reason">
        ///     The reason for the UMS invocation.
        /// </param>
        /// <param name="activationPayload">
        ///     The activation payload (depends on reason)
        /// </param>
        /// <param name="pData">
        ///     The context (the primary pointer)
        /// </param>
        static void NTAPI PrimaryInvocation(UMS_SCHEDULER_REASON reason, ULONG_PTR activationPayload, PVOID pData);

        /// <summary>
        ///     The primary thread.
        /// </summary>
        /// <param name="pContext">
        ///     The TransmogrifiedPrimary that the primary manages.
        /// </param>
        static DWORD CALLBACK PrimaryMain(LPVOID pContext);

    };

    /// <summary>
    ///     A transmogrified primary which places itself upon the transmogrificator's cache at the end of each transmogrification.
    /// </summary>
    class CachedTransmogrifiedPrimary : public TransmogrifiedPrimary
    {
    public:

        /// <summary>
        ///     Construct a new cached transmogrified primary.  The primary will be placed back upon the cache of the specified transmogrificator
        ///     at the end of each transmogrification.
        /// </summary>
        CachedTransmogrifiedPrimary(Transmogrificator *pTransmogrificator);

    protected:

        /// <summary>
        ///     Called when a transmogrification is complete.
        /// </summary>
        virtual void CompletedTransmogrification();

    private:

        friend class Transmogrificator;

        // An entry for this primary on the transmogrificator's cache.
        SLIST_ENTRY m_cacheEntry;

        Transmogrificator *m_pTransmogrificator;
    };

} // namespace details
} // namespace Concurrency
