/************************************************************************************************

  Copyright (c) 2001 Microsoft Corporation

File Name:      ThdPool.cpp
Abstract:       Implementation of the thread pool (CThreadPool class)
Notes:          
History:        08/01/2001 Created by Hao Yu (haoyu)

************************************************************************************************/

#include <stdafx.h>

#include <ThdPool.hxx>
#include <SockPool.hxx>
#include <GlobalDef.h>


// The common thread in the thread pool
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{    
    ASSERT(NULL != lpParameter);
    DWORD dwBytesRcvd=0;
    DWORD Flags=0;
    PIO_CONTEXT pIoContext=NULL;
    LPOVERLAPPED pOverlapped=NULL;
    HANDLE hCompPort=lpParameter;
    HRESULT hr=CoInitializeEx(NULL, COINIT_MULTITHREADED); 
    if(FAILED(hr))
    {
        g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
                       EVENT_POP3_COM_INIT_FAIL);
        ExitProcess(hr);
    }
    while(1)
    {

        GetQueuedCompletionStatus(hCompPort,
                                  &dwBytesRcvd,
                                   (PULONG_PTR)(&pIoContext),  
                                  &pOverlapped,
                                  INFINITE);    
        //We don't care about return value
        //since we use the failure case to clean up the IO Context
        if(NULL == pIoContext || SERVICE_STOP_PENDING == g_dwServerStatus)
        { 
            // This is a shutdown signal
            break;
        }
        g_PerfCounters.DecPerfCntr(e_gcFreeThreadCnt);
        pIoContext->m_pCallBack((PULONG_PTR)pIoContext, pOverlapped, dwBytesRcvd);
        g_PerfCounters.IncPerfCntr(e_gcFreeThreadCnt);
        
    } 
    
    g_PerfCounters.DecPerfCntr(e_gcFreeThreadCnt);
    CoUninitialize();
    return 0;

}



CThreadPool::CThreadPool()
{
    InitializeCriticalSection(&m_csInitGuard);
    m_hIOCompPort = NULL;
    m_phTdArray   = NULL;
    m_dwTdCount   = 0;
    m_bInit       = FALSE;
}


CThreadPool::~CThreadPool()
{
   if(m_bInit)
   {
       Uninitialize();
   }
   DeleteCriticalSection(&m_csInitGuard);
}

// Job done in this function:
// 1) Calculate the number of threads need to be created,
//    dwThreadPerProcessor * number of processors of the machine
// 2) Create the IO Completion port
// 3) Create threads  
BOOL CThreadPool::Initialize(DWORD dwThreadPerProcessor)
{
    int i;
    BOOL bRtVal=TRUE;
    SYSTEM_INFO SystemInfo;

    EnterCriticalSection(&m_csInitGuard);
    if(!m_bInit)
    {
        //Get the number of processors of the machine
        GetSystemInfo(&SystemInfo);
    

        if( dwThreadPerProcessor == 0  ||
            dwThreadPerProcessor > MAX_THREAD_PER_PROCESSOR )
        {
            dwThreadPerProcessor = 1;
        }

        m_dwTdCount = SystemInfo.dwNumberOfProcessors * dwThreadPerProcessor;
    

        // Create the IO Completion Port
        m_hIOCompPort = CreateIoCompletionPort  (
                        INVALID_HANDLE_VALUE,
                        NULL,
                        NULL,
                        m_dwTdCount);
        if (NULL == m_hIOCompPort)
        {
            g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
                                   POP3SVR_FAIL_TO_CREATE_IO_COMP_PORT, 
                                   GetLastError());
            goto EXIT;
        }

        m_phTdArray=new HANDLE[m_dwTdCount];

        if( NULL == m_phTdArray)
        {
            
            g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
                                   POP3SVR_NOT_ENOUGH_MEMORY);
            goto EXIT;
        }

        // Create the threads
        for (i=0;i<m_dwTdCount; i++)
        {
            m_phTdArray[i] = CreateThread(
                            NULL,
                            0,
                            ThreadProc,
                            m_hIOCompPort,
                            0,
                            NULL);
            if(NULL == m_phTdArray[i])
            {
                g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL, 
                                       POP3SVR_FAILED_TO_CREATE_THREAD, 
                                       GetLastError());
                goto EXIT;
            }        
        }
        m_bInit=TRUE;
    }
    //Set the total free thread count
    g_PerfCounters.SetPerfCntr(e_gcFreeThreadCnt, m_dwTdCount);
    LeaveCriticalSection(&m_csInitGuard);
    return TRUE;

EXIT:

    //In case of error, cleanup and exit
    if(NULL != m_phTdArray)
    {
        for(i=0; i<m_dwTdCount && m_phTdArray[i]; i++ )
        {
            if(m_phTdArray[i]!=NULL)
            {
                TerminateThread(m_phTdArray[i], -1);
                CloseHandle(m_phTdArray[i]);
            }
        }
        delete[](m_phTdArray);
        m_phTdArray=NULL;
    }
    
    if(m_hIOCompPort)
    {
        CloseHandle(m_hIOCompPort);
        m_hIOCompPort=NULL;
    }
    LeaveCriticalSection(&m_csInitGuard);
    return FALSE;
}

// Terminate all threads and delete the completion port.
void CThreadPool::Uninitialize()
{
    int i;
    BOOL bFailedExit=FALSE;
    DWORD dwRt;
    DWORD dwStatus=0;
    EnterCriticalSection(&m_csInitGuard);
    if(m_bInit)
    {
        if(NULL != m_phTdArray)
        {
            for(i=0; i<m_dwTdCount; i++ )
            {
                PostQueuedCompletionStatus(m_hIOCompPort, 0, NULL, NULL);
            }
            dwRt=WaitForMultipleObjects(m_dwTdCount, 
                                        m_phTdArray,
                                        TRUE,
                                        SHUTDOWN_WAIT_TIME);

            if( (WAIT_TIMEOUT == dwRt) ||
                (WAIT_FAILED  == dwRt) )
            {
                for(i=0; i<m_dwTdCount; i++ )
                {
                    //In case some thread did not exit after the wait time
                    //terminate threads by force
                    if(NULL!= m_phTdArray[i])
                    {
                        if( !GetExitCodeThread(m_phTdArray[i], &dwStatus) ||
                            (STILL_ACTIVE==dwStatus))
                        {
                            // This is a bad case, however we can not wait 
                            // forever, cleanup won't be complete in this case.
                            TerminateThread(m_phTdArray[i],0);
                            bFailedExit=TRUE;
                        }
                        CloseHandle(m_phTdArray[i]);
                    }
                }
            }
            else
            {
                for(i=0; i<m_dwTdCount; i++ )
                {
                    if(NULL!= m_phTdArray[i])
                    {
                        CloseHandle(m_phTdArray[i]);
                    }
                }
            }
            delete[](m_phTdArray);
            m_phTdArray=NULL;
        }
    
        if(m_hIOCompPort)
        {
            CloseHandle(m_hIOCompPort);
            m_hIOCompPort=NULL;
        }
        m_bInit=FALSE;
    }
    LeaveCriticalSection(&m_csInitGuard);
    if(bFailedExit)
    {
        g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
                               EVENT_POP3_SERVER_STOP_ERROR, 
                               E_UNEXPECTED);

        ExitProcess(E_UNEXPECTED);
    }
}

// Associate an IO Context and the IO handle contained 
// with the IO Completion port
BOOL CThreadPool::AssociateContext(PIO_CONTEXT pIoContext)
{
    if(!m_bInit)
    {
        return FALSE;
    }

    return (NULL!=CreateIoCompletionPort(
                       (HANDLE)(pIoContext->m_hAsyncIO),
                       m_hIOCompPort,
                       (ULONG_PTR)pIoContext,
                       m_dwTdCount));
}