/*++

Copyright (c) 2000-2001,  Microsoft Corporation  All rights reserved.

Module Name:

    threads.c

Abstract:

    This file contains functions that support our threading efforts

    Functions found in this file:
        GetThreadInfoSafe
        UninitThread

Revision History:

    01 Mar 2001    v-michka    Created.

--*/

#include "precomp.h"

//
// The MEM structure. We store a linked list of these. In our
// most optimal case, this is a list with just one MEM in it.
//
struct MEM 
{
    LPGODOTTLSINFO alloc;   // the allocation to store
    struct MEM* next;       // Pointer to the next MEM to chain to
};

struct MEM* m_memHead;


/*-------------------------------
    GetThreadInfoSafe

    Returns our TLS info, and if it has never been gotten, 
    allocates it (if the caller specifies) and returns the 
    newly allocated info.
-------------------------------*/
LPGODOTTLSINFO GetThreadInfoSafe(BOOL fAllocate)
{
    LPGODOTTLSINFO lpgti = NULL;
    DWORD dwLastError = ERROR_SUCCESS;

    // Use SEH around our critical section since low memory 
    // situations can cause a STATUS_INVALID_HANDLE exception
    // to be raise. We will simply set the last error for this 
    // case so the client can always know why we failed
    // CONSIDER: Use ERROR_LOCK_FAILED or ERROR_LOCKED here?
    __try
    {
        EnterCriticalSection(&g_csThreads);
    
        lpgti = TlsGetValue(g_tls);
        dwLastError = GetLastError();

        if(!lpgti)
        {
            if(fAllocate)
            {
                struct MEM* newThreadMem;
                
                lpgti = GodotHeapAlloc(sizeof(GODOTTLSINFO));
                if(!lpgti)
                    dwLastError = ERROR_OUTOFMEMORY;
                else
                {
                    // First lets add the block to our 
                    // handy linked list of allocations.
                    if(newThreadMem = GodotHeapAlloc(sizeof(struct MEM)))
                    {
                        newThreadMem->alloc = lpgti;
                        newThreadMem->next = m_memHead;
                        m_memHead = newThreadMem;

                        // Now lets store it in the TLS slot.
                        dwLastError = GetLastError();
                        TlsSetValue(g_tls, lpgti);
                    }
                    else
                        dwLastError = ERROR_OUTOFMEMORY;

                    if(dwLastError != ERROR_SUCCESS)
                    {
                        // we failed somehow, so clean it all up
                        GodotHeapFree(lpgti);
                        m_memHead = m_memHead->next;
                        GodotHeapFree(newThreadMem);
                        lpgti = NULL;
                    }
                }
             }
            else
                dwLastError=ERROR_OUTOFMEMORY;

        }
    }

    __finally
    {
        LeaveCriticalSection(&g_csThreads);
    }

    if(lpgti == NULL)
    {
        SetLastError(dwLastError);
        return(NULL);
    }
    return(lpgti);
}


/*-------------------------------
    UninitThread
-------------------------------*/
void UninitThread(void)
{
    LPGODOTTLSINFO lpgti;

    if(g_tls)
    {
        // don't alloc if its not there!
        lpgti = GetThreadInfoSafe(FALSE);

        // Use SEH around our critical section since low memory 
        // situations can cause a STATUS_INVALID_HANDLE exception
        // to be raise. Note that if we fail to enter our CS that
        // missing out on this alloc is the least of our problems.
        // We will get a second chance at process close to free 
        // it up, if we ever get there.
        __try
        {
            EnterCriticalSection(&g_csThreads);
            if(lpgti)
            {
                struct MEM* current = m_memHead;
            
                // Clean up that ol' heap allocated memory
                GodotHeapFree(lpgti);

                while(current != NULL)
                {
                    if(current->alloc == lpgti)
                    {
                        // Must handle the head case separately
                        m_memHead = current->next;
                        current->alloc = NULL;
                        GodotHeapFree(current);
                        break;
                    }
                
                    if((current->next != NULL) && (current->next->alloc == lpgti))
                    {
                        // The next one in line is the 
                        // one we want to free up
                        current->next = current->next->next;
                        current->next->alloc = NULL;
                        GodotHeapFree(current->next);
                        break;
                    }

                    current = current->next;
                }
            
                TlsSetValue(g_tls, NULL);
            }
        }
        __finally
        {
            LeaveCriticalSection(&g_csThreads);
        }
    }

    return;
}

/*-------------------------------
    UninitAllThreads

    Deletes our entire linked list of allocations. Note that we
    can only call TlsSetValue on the current thread, not others.
    However, this function will invalidate any pointers in other
    threads so this function should NEVER be called until process
    close.
-------------------------------*/
void UninitAllThreads(void)
{
    struct MEM* current;
    struct MEM* next;

    __try
    {
        EnterCriticalSection(&g_csThreads);
        current = m_memHead;

        while (current != NULL)
        {
            next = current->next;
            GodotHeapFree(current->alloc);
            GodotHeapFree(current);
            current = next;
        }
        m_memHead = NULL;
    }
    __finally
    {
        LeaveCriticalSection(&g_csThreads);
    }
}