//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. 
//

/*++



Module Name:

    palobjbase.cpp

Abstract:
    PAL object base class



--*/

#include "palobjbase.hpp"
#include "pal/malloc.hpp"
#include "pal/dbgmsg.h"

SET_DEFAULT_DEBUG_CHANNEL(PAL);

using namespace CorUnix;

CObjectType* CObjectType::s_rgotIdMapping[ObjectTypeIdCount];

/*++
Function:
  CPalObjectBase::Initialize

  Performs possibly-failing initialization for a newly-constructed
  object

Parameters:
  pthr -- thread data for calling thread
  poa -- the object attributes (e.g., name) for the object
--*/

PAL_ERROR
CPalObjectBase::Initialize(
    CPalThread *pthr,
    CObjectAttributes *poa
    )
{
    PAL_ERROR palError = NO_ERROR;

    _ASSERTE(NULL != pthr);
    _ASSERTE(NULL != poa);

    ENTRY("CPalObjectBase::Initialize"
        "(this = %p, pthr = %p, poa = %p)\n",
        this,
        pthr,
        poa
        );

    if (0 != m_pot->GetImmutableDataSize())
    {
        m_pvImmutableData = InternalMalloc(m_pot->GetImmutableDataSize());
        if (NULL != m_pvImmutableData)
        {
            ZeroMemory(m_pvImmutableData, m_pot->GetImmutableDataSize());
        }
        else
        {
            ERROR("Unable to allocate immutable data\n");
            palError = ERROR_OUTOFMEMORY;
            goto IntializeExit;
        }
    }

    if (0 != m_pot->GetProcessLocalDataSize())
    {
        palError = m_sdlLocalData.Initialize();
        if (NO_ERROR != palError)
        {
            ERROR("Unable to initialize local data lock!\n");
            goto IntializeExit;
        }
        
        m_pvLocalData = InternalMalloc(m_pot->GetProcessLocalDataSize());
        if (NULL != m_pvLocalData)
        {
            ZeroMemory(m_pvLocalData, m_pot->GetProcessLocalDataSize());
        }
        else
        {
            ERROR("Unable to allocate local data\n");
            palError = ERROR_OUTOFMEMORY;
            goto IntializeExit;
        }
    }

    if (0 != poa->sObjectName.GetStringLength())
    {
        palError = m_oa.sObjectName.CopyString(&poa->sObjectName);
    }

IntializeExit:

    LOGEXIT("CPalObjectBase::Initialize returns %d\n", palError);

    return palError;
}

/*++
Function:
  CPalObjectBase::GetObjectType

  Returns the type of the object
--*/

CObjectType *
CPalObjectBase::GetObjectType(
    VOID
    )
{
    ENTRY("CPalObjectBase::GetObjectType(this = %p)\n", this);
    LOGEXIT("CPalObjectBase::GetObjectType returns %p\n", m_pot);
    
    return m_pot;
}

/*++
Function:
  CPalObjectBase::GetObjectAttributes

  Returns the attributes of the object
--*/

CObjectAttributes *
CPalObjectBase::GetObjectAttributes(
    VOID
    )
{
    ENTRY("CPalObjectBase::GetObjectAttributes(this = %p)\n", this);
    LOGEXIT("CPalObjectBase::GetObjectAttributes returns %p\n", &m_oa);
    
    return &m_oa;
}

/*++
Function:
  CPalObjectBase::GetImmutableData

  Provides the caller access to the object's immutable data (if any)

Parameters:
  ppvImmutableData -- on success, receives a pointer to the object's
    immutable data
--*/

PAL_ERROR
CPalObjectBase::GetImmutableData(
    void **ppvImmutableData             // OUT
    )
{
    _ASSERTE(NULL != ppvImmutableData);
    
    ENTRY("CPalObjectBase::GetImmutableData"
        "(this = %p, ppvImmutableData = %p)\n",
        this,
        ppvImmutableData
        );
    
    _ASSERTE(0 < m_pot->GetImmutableDataSize());

    *ppvImmutableData = m_pvImmutableData;

    LOGEXIT("CPalObjectBase::GetImmutableData returns %d\n", NO_ERROR);

    return NO_ERROR;
}

/*++
Function:
  CPalObjectBase::GetProcessLocalData

  Provides the caller access to the object's local data (if any)

Parameters:
  pthr -- thread data for calling thread
  eLockRequest -- specifies if the caller desires a read lock or a
    write lock on the data (currently ignored)
  ppDataLock -- on success, receives a pointer to the data lock instance
    for the local data
  ppvProcssLocalData -- on success, receives a pointer to the local data
--*/

PAL_ERROR
CPalObjectBase::GetProcessLocalData(
    CPalThread *pthr,
    LockType eLockRequest,
    IDataLock **ppDataLock,             // OUT
    void **ppvProcessLocalData          // OUT
    )
{
    _ASSERTE(NULL != pthr);
    _ASSERTE(ReadLock == eLockRequest || WriteLock == eLockRequest);
    _ASSERTE(NULL != ppDataLock);
    _ASSERTE(NULL != ppvProcessLocalData);
    
    ENTRY("CPalObjectBase::GetProcessLocalData"
        "(this = %p, pthr = %p, eLockRequest = %d, ppDataLock = %p,"
        " ppvProcessLocalData = %p)\n",
        this,
        pthr,
        eLockRequest,
        ppDataLock,
        ppvProcessLocalData
        );
    
    _ASSERTE(0 < m_pot->GetProcessLocalDataSize());

    m_sdlLocalData.AcquireLock(pthr, ppDataLock);
    *ppvProcessLocalData = m_pvLocalData;

    LOGEXIT("CPalObjectBase::GetProcessLocalData returns %d\n", NO_ERROR);
    
    return NO_ERROR;
}

/*++
Function:
  CPalObjectBase::AddReference

  Increments the object's reference count. The updated count is returned
  for diagnostic purposes only
--*/

DWORD
CPalObjectBase::AddReference(
    void
    )
{
    LONG lRefCount;

    ENTRY("CPalObjectBase::AddReference(this = %p)\n", this);

    _ASSERTE(m_lRefCount > 0);
    lRefCount = InterlockedIncrement(&m_lRefCount);

    LOGEXIT("CPalObjectBase::AddReference returns %d\n", lRefCount);

    return lRefCount;
}

/*++
Function:
  CPalObjectBase::ReleaseReference

  Decrements the object's reference count. The updated count is returned
  for diagnostic purposes only

Parameters:
  pthr -- thread data for calling thread
--*/

DWORD
CPalObjectBase::ReleaseReference(
    CPalThread *pthr
    )
{
    LONG lRefCount;

    _ASSERTE(NULL != pthr);

    ENTRY("CPalObjectBase::ReleaseReference"
        "(this = %p, pthr = %p)\n",
        this,
        pthr
        );

    AcquireObjectDestructionLock(pthr);

    _ASSERTE(m_lRefCount > 0); 

    //
    // Even though object destruction takes place under a lock
    // we still need to use an interlocked decrement, as AddRef
    // operates lock free
    //

    lRefCount = InterlockedDecrement(&m_lRefCount);

    if (0 == lRefCount)
    {
        bool fCleanupSharedState = ReleaseObjectDestructionLock(pthr, TRUE);

        //
        // We need to do two things with the calling thread data here:
        // 1) store it in m_pthrCleanup so it is available to the destructors
        // 2) Add a reference to it before starting any cleanup, and release
        //    that reference afterwords.
        //
        // Step 2 is necessary when we're cleaning up the thread object that
        // represents the calling thread -- it ensures that the thread data
        // is available throughout the entire cleanup process.
        //

        m_pthrCleanup = pthr;
        pthr->AddThreadReference();

        if (NULL != m_pot->GetObjectCleanupRoutine())
        {
            (*m_pot->GetObjectCleanupRoutine())(
                pthr,
                static_cast<IPalObject*>(this),
                FALSE,
                fCleanupSharedState
                );
        }

        InternalDelete(this);

        pthr->ReleaseThreadReference();
    }
    else
    {       
        ReleaseObjectDestructionLock(pthr, FALSE);
    }

    LOGEXIT("CPalObjectBase::ReleaseReference returns %d\n", lRefCount);

    return lRefCount;
}

/*++
Function:
  CPalObjectBase::~CPalObjectBase

  Object destructor
--*/

CPalObjectBase::~CPalObjectBase()
{
    ENTRY("CPalObjectBase::~CPalObjectBase(this = %p)\n", this);

    if (NULL != m_pvImmutableData)
    {
        InternalFree(m_pvImmutableData);
    }

    if (NULL != m_pvLocalData)
    {
        InternalFree(m_pvLocalData);
    }

    if (NULL != m_oa.sObjectName.GetString())
    {
        m_oa.sObjectName.FreeBuffer();
    }

    LOGEXIT("CPalObjectBase::~CPalObjectBase\n");
}

