/*-----------------------------------------------------------------------------

   Copyright    (c)    1995-1997    Microsoft Corporation

   Module  Name :
       waminfo.hxx

   Abstract:
       Header file for WAMINFO object.

   Author:
       David Kaplan    ( DaveK )     11-Mar-1997

   Environment:
       User Mode - Win32

   Project:
       W3 services DLL

-----------------------------------------------------------------------------*/


/*-----------------------------------------------------------------------------
 Forward references

-----------------------------------------------------------------------------*/

#include <lkrhash.h>
#include <svmap.h>
#include <issched.hxx>

#ifndef __W3SVC_WAMINFO_HXX__
#define __W3SVC_WAMINFO_HXX__
#include "ptable.hxx"

interface       IWam;

/*-----------------------------------------------------------------------------
External definitions

-----------------------------------------------------------------------------*/
typedef
LONG (WINAPI * PFN_INTERLOCKED_COMPARE_EXCHANGE)
    (
    LONG *Destination,
    LONG Exchange,
    LONG Comperand
    );

extern PFN_INTERLOCKED_COMPARE_EXCHANGE g_pfnInterlockedCompareExchange;

HRESULT
CreateWamRequestInstance
    (
    HTTP_REQUEST    *       pHttpRequest,
    EXEC_DESCRIPTOR *       pExec,
    const STR *             pstrPath,
    CWamInfo *              pWamInfo,
    WAM_REQUEST**           ppWamRequestOut
    );


/*-----------------------------------------------------------------------------
WAM States      Explaination

WIS_START       This state is set in WamInfo constructor, and changed to WIS_RUNNING
                if Init() succeeded.  Therefore, a WamInfo in either hashtable or dyinglist
                can not have this state.

WIS_RUNNING     This state indicates the WAMINFO in the hashtable is ok to serving requests.

WIS_REPAIR      This state indicates a thread is currently repair WAMINFO after it detects the outproc
                Wam pointer is nolonger reachable via RPC(a crash).  This state is only
                applied to WamInfoOutProc.

WIS_PAUSE       This state indicates the application is paused by metabase setting, therefore,
                the WamInfo should be deleted such that next request will refresh the metabase
                setting again.

WIS_CPUPAUSE    This state indicates the application is paused by Job object control.  An application
                is in a zombie state that it does not have IWAM pointer(and other stuff) since the
                out-proc mtx process is terminated.
                
WIS_SHUTDOWN    This state indicates the WamInfo is in a shutdown stage.  Therefore, any new requests
                need to be rejected.

WIS_ERROR       This state indicates the WamInfo is in some weired error state.(Currently, it is not
                being used).

WIS_END         This state indicates the WamInfo is in destructor mode.


General rules to change the state:

Use ChangeToState(oldstate, newstate), check whether the return value is same as oldstate.
-----------------------------------------------------------------------------*/
enum
    {
    WIS_START   =   0,
    WIS_RUNNING,
    WIS_REPAIR,
    WIS_PAUSE,
    WIS_CPUPAUSE,
    WIS_ERROR,
    WIS_SHUTDOWN,
    WIS_END,
    WIS_MAX_STATE
    };

/*-----------------------------------------------------------------------------
class CWamInfo:

One-to-One relation with Wam object.
-----------------------------------------------------------------------------*/
class CWamInfo
{
public:

    CWamInfo
        (
        const STR &strMetabasePath,
        BOOL fInProcess,
        BOOL fInPool,
        BOOL fEnableTryExcept,
        REFGUID clsidWam
        );

    virtual ~CWamInfo(void);

    void    Reference(void);
    void    Dereference(void);
    void    RecycleLock(void);
    void    RecycleUnlock(void);
    // ------------------------------------------------------------
    //  WAM INFO's public functions
    // ------------------------------------------------------------

    HRESULT     CreateWamInstance( DWORD dwContext );


    virtual HRESULT Init
                    (
                    WCHAR* wszPackageId,
                    DWORD  pidInetInfo
                    );

    virtual HRESULT UnInit(void);

    virtual void    LeaveOOPZone(WAM_REQUEST *      pWamRequest, BOOL fRecord);

    virtual HRESULT GetStatistics
                    (
                    DWORD   Level,
                    LPWAM_STATISTICS_INFO pWamStatsInfo
                    );

    virtual void    LoopWaitForActiveRequests
                    (
                    INT     cIgnoreRefs
                    );

    virtual HRESULT StartShutdown
                    (
                    INT     cIgnoreRefs
                    );    // NOTE StartShutdown is non-virtual so derived class can use it

    virtual VOID    NotifyGetInfoForName
                    (
                    IN LPCSTR   pszServerVariable
                    );

    virtual HRESULT DoProcessRequestCall
                    (
                    IN IWam *           pIWam,
                    IN WAM_REQUEST *    pWamRequest,
                    OUT BOOL *          pfHandled
                    );

    HRESULT ProcessAsyncIO
                    (
                    WAM_REQUEST*    pWamRequest,
#ifdef _WIN64
                    UINT64          pWamExecInfoIn, // WamExecInfo *
#else
                    ULONG_PTR       pWamExecInfoIn, // WamExecInfo *
#endif
                    DWORD           dwStatus,
                    DWORD           cbWritten,
                    LPBYTE          lpOopReadData = NULL
                    );


    HRESULT ProcessWamRequest
                    (
                    HTTP_REQUEST    *       pHttpRequest,
                    EXEC_DESCRIPTOR *       pExec,
                    const STR *             pstrPath,
                    BOOL *                  pfHandled,
                    BOOL *                  pfFinished
                    );

    DWORD       ChangeToState(DWORD dwState);
    DWORD       ChangeToState(DWORD dwOldState, DWORD dwNewState);
    DWORD       QueryState(void) const;
	DWORD		QueryCurrentRequests(void) const;
    virtual VOID    ClearMembers();

    IWam*       QueryIWam(void) const;
    CProcessEntry*  QueryProcessEntry(void) const;
    DWORD       PidWam(void) const;
    DWORD       FInProcess(void) const;
    HANDLE      HWamProcess(void) const;
    const STR & QueryApplicationPath(void) const;
    BOOL        FCurrentStateValid(void) const;

    LPCSTR  CWamInfo::QueryKey() const;
    void    Print(void) const;

    void        Recycle
                (
                DWORD dwTimeInSec
                );

    DWORD       QueryShutdownTimeLimit(void) const;

protected:
    virtual HRESULT     PreProcessWamRequest
                        (
                        IN  WAM_REQUEST*    pWamRequest,
                        IN  HTTP_REQUEST*   pHttpRequest,
                        OUT IWam**          ppIWam,
                        OUT BOOL*           pfHandled
                        );

    virtual HRESULT     PostProcessRequest
                        (
                        IN  HRESULT         hrIn,
                        IN  WAM_REQUEST *   pWamRequest
                        );

    virtual HRESULT     PreProcessAsyncIO
                        (
                        IN  WAM_REQUEST *   pWamRequest,
                        OUT IWam **         ppIWam
                        );

public:
    LIST_ENTRY      ListEntry;              // Double link List node for linking to DyingList.
    LIST_ENTRY      leProcess;              // Double Link List node for linking to the process entry.
    BOOL            m_fRecycled;
    DWORD           m_dwRecycleSchedulerCookie;

protected:
    DWORD           m_dwSignature;

    IWam*           m_pIWam;

    DWORD           m_dwIWamGipCookie;  // GIP cookie to get thread-valid pIWams
    CProcessEntry*  m_pProcessEntry;  
    LONG            m_cRef;
    LONG            m_cCurrentRequests;
    LONG            m_cTotalRequests;
    DWORD           m_dwState;
    BOOL            m_fInProcess;
    BOOL            m_fInPool;
    BOOL            m_fEnableTryExcept;
    BOOL            m_fShuttingDown;
    STR             m_strApplicationPath;
    CLSID           m_clsidWam;
    LONG            m_cMaxRequests;
    DWORD           m_dwShutdownTimeLimit;
	DWORD			m_dwRecycleTime;

    static  BOOL    m_rgValidState[WIS_MAX_STATE];

    CRITICAL_SECTION    m_csRecycleLock;
};

inline void CWamInfo::Reference(void)
    {
//  DBGPRINTF((DBG_CONTEXT, "CWamInfo %p AddRef, m_cRef %d\n", this, m_cRef+1));
    InterlockedIncrement(&m_cRef);
    }

inline void CWamInfo::Dereference(void)
    {
    DBG_ASSERT(m_cRef > 0);
//  DBGPRINTF((DBG_CONTEXT, "CWamInfo %p Release, m_cRef %d\n", this, m_cRef-1));
    if ( 0 == InterlockedDecrement(&m_cRef) )
		{
		delete this;
		}
    }

inline DWORD CWamInfo::PidWam() const
    {
        return( m_pProcessEntry->QueryProcessId());
    }

inline HANDLE CWamInfo::HWamProcess() const
    {
        return( m_pProcessEntry->QueryProcessHandle() );
    }

inline DWORD CWamInfo::FInProcess() const
    {
        return( m_fInProcess );
    }

inline BOOL CWamInfo::FCurrentStateValid(void) const
    {
        return (m_rgValidState[m_dwState]);
    }

inline DWORD CWamInfo::QueryState(void) const
    {
        return (m_dwState);
    }

inline DWORD CWamInfo::QueryCurrentRequests(void) const
	{
	return (m_cCurrentRequests);
	}

inline CProcessEntry* CWamInfo::QueryProcessEntry(void) const
    {
    return m_pProcessEntry;
    }

inline DWORD CWamInfo::ChangeToState(DWORD eState)
    {
        return (DWORD)InterlockedExchange((LONG *)&m_dwState, eState);
    }

inline DWORD CWamInfo::ChangeToState(DWORD eOldState, DWORD eNewState)
    {
        return (DWORD)(*g_pfnInterlockedCompareExchange)((LONG *)&m_dwState, (LONG)eNewState, (LONG)eOldState);
    }

inline IWam* CWamInfo::QueryIWam(void) const
    {
        return m_pIWam;
    }

inline const STR &
CWamInfo::QueryApplicationPath(void) const
    {
    // returns the STR object containing the Application Root Path
    return ( m_strApplicationPath);
    }

inline void     CWamInfo::LeaveOOPZone(WAM_REQUEST *    pWamRequest, BOOL fRecord)
    {
    //  No Op.
    }

inline HRESULT     CWamInfo::PostProcessRequest
(
IN  HRESULT         hrIn,
IN  WAM_REQUEST *   pWamRequest
)
    {
    return NOERROR;
    }

inline DWORD    CWamInfo::QueryShutdownTimeLimit(void) const
    {
    return m_dwShutdownTimeLimit;
    }

inline VOID     CWamInfo::RecycleLock(void)
    {
    Reference();
    EnterCriticalSection( &m_csRecycleLock );
    }

inline VOID     CWamInfo::RecycleUnlock(void)
    {
    LeaveCriticalSection( &m_csRecycleLock );
    Dereference();
    }

class CWamInfoHash
    : public CTypedHashTable<CWamInfoHash, CWamInfo, const char*>
{
public:
    static const char*      ExtractKey(const CWamInfo* pWamInfo);

    static DWORD            CalcKeyHash(const char* pszKey);

    static bool             EqualKeys(const char* pszKey1, const char* pszKey2);

    static void             AddRefRecord(CWamInfo* pWamInfo, int nIncr);

    CWamInfoHash
        (
        double      maxload,        // Bound on average chain length,
        size_t      initsize,       // Initial size of Hash Table
        size_t      num_subtbls     // #subordinate hash tables.
        )
        : CTypedHashTable<CWamInfoHash, CWamInfo, const char*>
            ("WamInfo", maxload, initsize, num_subtbls)
            {}
};

inline const char*
CWamInfoHash::ExtractKey(const CWamInfo* pWamInfo)
    {
    return pWamInfo->QueryKey();
    }

inline DWORD
CWamInfoHash::CalcKeyHash(const char* pszKey)
    {
    return HashStringNoCase(pszKey);
    }

inline bool
CWamInfoHash::EqualKeys(const char* pszKey1, const char* pszKey2)
    {
    return (_stricmp(pszKey1, pszKey2) == 0);
    }

inline void
CWamInfoHash::AddRefRecord(CWamInfo* pWamInfo, int nIncr)
    {
    if (nIncr == 1)
        {
        pWamInfo->Reference();
        }
    else
        {
        pWamInfo->Dereference();
        }
    }

//========================   CWamInfoOutProc    ====================================

/*-----------------------------------------------------------------------------
//  COOPWamReqList
//
//  class that contains link list head for OOP Wam Requests of a WAM instance.
//
-----------------------------------------------------------------------------*/
class COOPWamReqList
{
public:
    COOPWamReqList(void);
    ~COOPWamReqList(void);

    BOOL FTimeToCleanup(DWORD dwCurrentTime);
    BOOL FActive(void);
    void SetTimeStamp(void);

    LIST_ENTRY          m_leRecoverListLink;    // Link with other COOPWamReqList.
    LIST_ENTRY          m_leOOPWamReqListHead;  // Cleanup List Head
private:
    DWORD               m_dwTimeStamp;          // TimeStamp for Cleanup Scheduler
                                                    // (GetTickerCount)
    BOOL                m_fActive;              // FALSE if the corresponding Wam
                                                // crashed.
};

/*-----------------------------------------------------------------------------
//  COOPWamReqList::SetTimeStamp
//
//  Set time stamp when Out Proc Wam crashes.
-----------------------------------------------------------------------------*/
inline void
COOPWamReqList::SetTimeStamp(void)
    {
    m_dwTimeStamp = GetTickCount();
    m_fActive = FALSE;
    }

/*-----------------------------------------------------------------------------
//COOPWamReqList::FActive
//
//Return whether the list is Active or not(m_fActive).
-----------------------------------------------------------------------------*/
inline BOOL
COOPWamReqList::FActive(void)
    {
    return m_fActive;
    }

class CWamInfoOutProc : public CWamInfo
{
public:
    PLIST_ENTRY         m_pCurrentListHead; // pointer to an OOPWamReq ListHead
                                            // with current valid OOP WAM.
private:
    DWORD               m_fInRepair;        // flag indicates WamInfo in crash-recovery mode or not.
    DWORD               m_fNoMoreRecovery;  // flag indicates WamInfo failed to ReInitWam() or not.

    HANDLE              m_hPermitOOPEvent;  // Event that serve as a gate to OutofProc processing
                                            // Signaled when normal processing
                                            // Reset during the crash-recovery
    CRITICAL_SECTION    m_csList;           // Critical section for RecoverList.

    LIST_ENTRY          m_rgRecoverListHead;// pointer to an Array of cleanup list
    DWORD               m_dwThreshold;      // Threshold for number of active cleanup list.
    DWORD               m_dwWamVersion;     // Current valid WAM verion number.
    DWORD               m_cRecoverList;     // # of active cleanup list in the system(started with 1)
    DWORD               m_idScheduled;      // Scheduler ID for Schedule a work item.
    BOOL                m_fJobEnabled;
    PW3_SERVER_INSTANCE m_pwsiInstance;
    
    SV_CACHE_LIST       m_svCache;
    
public:
        CWamInfoOutProc
                (
                const STR &             strMetabasePath,
                BOOL                    fInProcess,
                BOOL                    fInPool,
                BOOL                    fEnableTryExcept,
                REFGUID                 clsidWam,
                DWORD                   dwThreshold,
                PW3_SERVER_INSTANCE     pwiInstance,
                BOOL                    fJobEnabled,
				DWORD                   dwPeriodicRestartRequests,
				DWORD                   dwPeriodicRestartTime,
				DWORD                   dwShutdownTimeLimit
				);

    virtual ~CWamInfoOutProc();

    HRESULT GetStatistics
                        (
                        DWORD     Level,
                        LPWAM_STATISTICS_INFO pWamStatsInfo
                        );

    static void     WINAPI CleanupScheduled(void *pContext);


    VOID    ClearMembers();

private:
    HRESULT     PreProcessWamRequest
                    (
                    IN  WAM_REQUEST*    pWamRequest,
                    IN  HTTP_REQUEST*   pHttpRequest,
                    OUT IWam**          ppIWam,
                    OUT BOOL*           pfHandled
                    );

    HRESULT     PostProcessRequest
                    (
                    IN  HRESULT         hrIn,
                    IN  WAM_REQUEST *   pWamRequest
                    );

    HRESULT     PreProcessAsyncIO
                    (
                    IN  WAM_REQUEST *   pWamRequest,
                    OUT IWam **         ppIWam
                    );

    HRESULT     Init
                    (
                    IN WCHAR* wszPackageId,
                    IN DWORD  pidInetInfo
                    );

    HRESULT     UnInit(void);

    VOID        NotifyGetInfoForName
                    (
                    IN LPCSTR   pszServerVariable
                    );

    HRESULT     DoProcessRequestCall
                    (
                    IN IWam *           pIWam,
                    IN WAM_REQUEST *    pWamRequest,
                    OUT BOOL *          pfHandled
                    );

    IWam *      EnterOOPZone(WAM_REQUEST *      pWamRequest, DWORD *pdwWamVersion, BOOL fRecord);
    void        LeaveOOPZone(WAM_REQUEST *      pWamRequest, BOOL fRecord);

    void        LoopWaitForActiveRequests(INT cIgnoreRefs);

    BOOL        FExceedCrashLimit(void);
    HRESULT     Repair(void);
    HRESULT     ReInitWam(void);
    BOOL        FinalCleanup(void);
    BOOL        CleanupAll(BOOL fScheduled);
    BOOL        Cleanup(COOPWamReqList *pCleanupList);

    void        LockList(void);
    void        UnLockList(void);

public:
    DWORD       GetWamVersion(void);
    BOOL        IsJobEnabled() {return m_fJobEnabled;};
};

inline BOOL
CWamInfoOutProc::FExceedCrashLimit(void)
{
    return (m_dwWamVersion >= m_dwThreshold || m_fNoMoreRecovery);
}

inline DWORD
CWamInfoOutProc::GetWamVersion(void)
{
    return ( m_dwWamVersion );
}

inline void
CWamInfoOutProc::LockList(void)
{
    EnterCriticalSection(&m_csList);
}

inline void
CWamInfoOutProc::UnLockList(void)
{
    LeaveCriticalSection(&m_csList);
}

#endif __W3SVC_WAMINFO_HXX__