blob: c33297460d8468b11d2917b60865faf3613555c6 [file]
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
/*++
Module Name:
shmobject.hpp
Abstract:
Shared memory based object
--*/
#include "shmobject.hpp"
#include "pal/malloc.hpp"
#include "pal/cs.hpp"
#include "pal/dbgmsg.h"
#include <stddef.h>
SET_DEFAULT_DEBUG_CHANNEL(PAL);
using namespace CorUnix;
/*++
Function:
CSharedMemoryObject::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
CSharedMemoryObject::Initialize(
CPalThread *pthr,
CObjectAttributes *poa
)
{
PAL_ERROR palError = NO_ERROR;
SHMObjData *psmod = NULL;
_ASSERTE(NULL != pthr);
_ASSERTE(NULL != poa);
ENTRY("CSharedMemoryObject::Initialize"
"(this = %p, pthr = %p, poa = %p)\n",
this,
pthr,
poa
);
palError = CPalObjectBase::Initialize(pthr, poa);
if (NO_ERROR != palError)
{
goto InitializeExit;
}
//
// If this is a named object it needs to go into the shared domain;
// otherwise it remains local
//
if (0 != m_oa.sObjectName.GetStringLength())
{
m_ObjectDomain = SharedObject;
palError = AllocateSharedDataItems(&m_shmod, &psmod);
if (NO_ERROR != palError || NULL == psmod)
{
goto InitializeExit;
}
}
if (0 != m_pot->GetSharedDataSize())
{
if (SharedObject == m_ObjectDomain)
{
//
// Map the shared data into our address space
//
if (NULL == psmod)
{
ASSERT("psmod should not be NULL");
palError = ERROR_INTERNAL_ERROR;
goto InitializeExit;
}
m_pvSharedData = SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjSharedData);
if (NULL == m_pvSharedData)
{
ASSERT("Unable to map shared data area\n");
palError = ERROR_INTERNAL_ERROR;
goto InitializeExit;
}
}
else
{
//
// Initialize the local shared data lock.
//
palError = m_sdlSharedData.Initialize();
if (NO_ERROR != palError)
{
ERROR("Failure initializing m_sdlSharedData\n");
goto InitializeExit;
}
//
// Allocate local memory to hold the shared data
//
m_pvSharedData = InternalMalloc(m_pot->GetSharedDataSize());
if (NULL == m_pvSharedData)
{
ERROR("Failure allocating m_pvSharedData (local copy)\n");
palError = ERROR_OUTOFMEMORY;
goto InitializeExit;
}
}
ZeroMemory(m_pvSharedData, m_pot->GetSharedDataSize());
}
InitializeExit:
LOGEXIT("CSharedMemoryObject::Initalize returns %d\n", palError);
return palError;
}
/*++
Function:
CSharedMemoryObject::InitializeFromExistingSharedData
Performs possibly-failing initialization for a newly-constructed
object that is to represent an existing object (i.e., importing
a shared object into this process)
The shared memory lock must be held when calling this method
Parameters:
pthr -- thread data for calling thread
poa -- the object attributes for the object
--*/
PAL_ERROR
CSharedMemoryObject::InitializeFromExistingSharedData(
CPalThread *pthr,
CObjectAttributes *poa
)
{
PAL_ERROR palError = NO_ERROR;
SHMObjData *psmod = NULL;
_ASSERTE(NULL != pthr);
_ASSERTE(NULL != poa);
ENTRY("CSharedMemoryObject::InitializeFromExistingSharedData"
"(this = %p, pthr = %p, poa = %p)\n",
this,
pthr,
poa
);
//
// This object is obviously shared...
//
m_ObjectDomain = SharedObject;
_ASSERTE(SHMNULL != m_shmod);
psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, m_shmod);
if (NULL == psmod)
{
ASSERT("Unable to map shared object data\n");
palError = ERROR_INTERNAL_ERROR;
goto InitializeFromExistingSharedDataExit;
}
//
// When we're being called on the duplicate handle path the passed
// in object attributes likely won't have an object name in it.
// If there is an object name in the shared data place that in the
// object attributs so that the constructed object has a local copy
// of the name
//
if (0 == poa->sObjectName.GetStringLength()
&& 0 != psmod->dwNameLength)
{
WCHAR *wsz;
wsz = SHMPTR_TO_TYPED_PTR(WCHAR, psmod->shmObjName);
if (NULL != wsz)
{
poa->sObjectName.SetStringWithLength(wsz, psmod->dwNameLength);
}
else
{
ASSERT("Unable to map object name\n");
palError = ERROR_INTERNAL_ERROR;
goto InitializeFromExistingSharedDataExit;
}
}
#if _DEBUG
else if (0 != psmod->dwNameLength)
{
WCHAR *wsz;
//
// Verify that the names are consistent
//
wsz = SHMPTR_TO_TYPED_PTR(WCHAR, psmod->shmObjName);
_ASSERTE(NULL != wsz);
_ASSERTE(0 == PAL_wcscmp(wsz, poa->sObjectName.GetString()));
}
#endif // debug
palError = CPalObjectBase::Initialize(pthr, poa);
if (NO_ERROR != palError)
{
goto InitializeFromExistingSharedDataExit;
}
if (SHMNULL != psmod->shmObjImmutableData)
{
VOID *pv = SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjImmutableData);
if (NULL != pv)
{
memcpy(m_pvImmutableData, pv, m_pot->GetImmutableDataSize());
}
else
{
ASSERT("Unable to map object immutable data\n");
palError = ERROR_INTERNAL_ERROR;
goto InitializeFromExistingSharedDataExit;
}
}
if (SHMNULL != psmod->shmObjSharedData)
{
m_pvSharedData = SHMPTR_TO_TYPED_PTR(VOID, psmod->shmObjSharedData);
if (NULL == m_pvSharedData)
{
ASSERT("Unable to map object shared data\n");
palError = ERROR_INTERNAL_ERROR;
goto InitializeFromExistingSharedDataExit;
}
}
if (NULL != m_pot->GetObjectInitRoutine())
{
palError = (*m_pot->GetObjectInitRoutine())(
pthr,
m_pot,
m_pvImmutableData,
m_pvSharedData,
m_pvLocalData
);
}
InitializeFromExistingSharedDataExit:
LOGEXIT("CSharedMemoryObject::InitalizeFromExistingSharedData returns %d\n", palError);
return palError;
}
/*++
Function:
CSharedMemoryObject::AllocatedSharedDataItems
Allocates and initialiazes the shared memory structures necessary to make an
object available to other processes
Parameters:
pshmObjData -- on success, receives the shared memory pointer for the
shared memory object data
ppsmod -- on success, receives the locally-mapped pointer for the shared
memory object data
--*/
PAL_ERROR
CSharedMemoryObject::AllocateSharedDataItems(
SHMPTR *pshmObjData,
SHMObjData **ppsmod
)
{
PAL_ERROR palError = NO_ERROR;
SHMPTR shmod = SHMNULL;
SHMObjData *psmod = NULL;
_ASSERTE(NULL != pshmObjData);
_ASSERTE(NULL != ppsmod);
ENTRY("CSharedMemoryObject::AllocateSharedDataItems"
"(this = %p, pshmObjData = %p, ppsmod = %p)\n",
this,
pshmObjData,
ppsmod
);
//
// We're about to make a number of shared memory allocations,
// so grab the lock for the entirety of the routine.
//
SHMLock();
shmod = SHMalloc(sizeof(SHMObjData));
if (SHMNULL == shmod)
{
ERROR("Unable to allocate m_shmod for new object\n");
palError = ERROR_OUTOFMEMORY;
goto AllocateSharedDataItemsExit;
}
psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, shmod);
_ASSERTE(NULL != psmod);
ZeroMemory(psmod, sizeof(*psmod));
psmod->eTypeId = m_pot->GetId();
psmod->lProcessRefCount = 1;
if (0 != m_oa.sObjectName.GetStringLength())
{
psmod->dwNameLength = m_oa.sObjectName.GetStringLength();
psmod->shmObjName = SHMWStrDup(m_oa.sObjectName.GetString());
if (SHMNULL == psmod->shmObjName)
{
ERROR("Unable to allocate psmod->shmObjName for new object\n");
palError = ERROR_OUTOFMEMORY;
goto AllocateSharedDataItemsExit;
}
}
if (0 != m_pot->GetImmutableDataSize())
{
//
// The shared copy of the object's immutable data will be initialized
// by CSharedMemoryObjectManager::RegisterObject or PromoteSharedData
//
psmod->shmObjImmutableData = SHMalloc(m_pot->GetImmutableDataSize());
if (SHMNULL == psmod->shmObjImmutableData)
{
ERROR("Unable to allocate psmod->shmObjImmutableData for new object\n");
palError = ERROR_OUTOFMEMORY;
goto AllocateSharedDataItemsExit;
}
}
if (0 != m_pot->GetSharedDataSize())
{
psmod->shmObjSharedData = SHMalloc(m_pot->GetSharedDataSize());
if (SHMNULL == psmod->shmObjSharedData)
{
ERROR("Unable to allocate psmod->shmObjSharedData for new object\n");
palError = ERROR_OUTOFMEMORY;
goto AllocateSharedDataItemsExit;
}
}
*pshmObjData = shmod;
*ppsmod = psmod;
AllocateSharedDataItemsExit:
if (NO_ERROR != palError && SHMNULL != shmod)
{
FreeSharedDataAreas(shmod);
}
SHMRelease();
LOGEXIT("CSharedMemoryObject::AllocateSharedDataItems returns %d\n", palError);
return palError;
}
/*++
Function:
CSharedMemoryObject::FreeSharedDataItems
Frees the shared memory structures referenced by the provided shared
memory pointer
Parameters:
shmObjData -- shared memory pointer to the structures to free
--*/
// static
void
CSharedMemoryObject::FreeSharedDataAreas(
SHMPTR shmObjData
)
{
SHMObjData *psmod;
_ASSERTE(SHMNULL != shmObjData);
ENTRY("CSharedMemoryObject::FreeSharedDataAreas"
"(shmObjData = %p)\n",
shmObjData
);
SHMLock();
psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, shmObjData);
_ASSERTE(NULL != psmod);
if (SHMNULL != psmod->shmObjImmutableData)
{
SHMfree(psmod->shmObjImmutableData);
}
if (SHMNULL != psmod->shmObjSharedData)
{
SHMfree(psmod->shmObjSharedData);
}
if (SHMNULL != psmod->shmObjName)
{
SHMfree(psmod->shmObjName);
}
SHMfree(shmObjData);
SHMRelease();
LOGEXIT("CSharedMemoryObject::FreeSharedDataAreas\n");
}
/*++
Function:
CSharedMemoryObject::PromoteShjaredData
Copies the object's state into the passed-in shared data structures
Parameters:
shmObjData -- shared memory pointer for the shared memory object data
psmod -- locally-mapped pointer for the shared memory object data
--*/
void
CSharedMemoryObject::PromoteSharedData(
SHMPTR shmObjData,
SHMObjData *psmod
)
{
_ASSERTE(SHMNULL != shmObjData);
_ASSERTE(NULL != psmod);
ENTRY("CSharedMemoryObject::PromoteSharedData"
"(this = %p, shmObjData = %p, psmod = %p)\n",
this,
shmObjData,
psmod);
//
// psmod has been zero-inited, so we don't need to worry about
// shmPrevObj, shmNextObj, fAddedToList, shmObjName, dwNameLength,
// or pvSynchData
//
psmod->lProcessRefCount = 1;
psmod->eTypeId = m_pot->GetId();
if (0 != m_pot->GetImmutableDataSize())
{
void *pvImmutableData;
pvImmutableData = SHMPTR_TO_TYPED_PTR(void, psmod->shmObjImmutableData);
_ASSERTE(NULL != pvImmutableData);
CopyMemory(
pvImmutableData,
m_pvImmutableData,
m_pot->GetImmutableDataSize()
);
}
if (0 != m_pot->GetSharedDataSize())
{
void *pvSharedData;
pvSharedData = SHMPTR_TO_TYPED_PTR(void, psmod->shmObjSharedData);
_ASSERTE(NULL != pvSharedData);
CopyMemory(
pvSharedData,
m_pvSharedData,
m_pot->GetSharedDataSize()
);
InternalFree(m_pvSharedData);
m_pvSharedData = pvSharedData;
}
m_shmod = shmObjData;
LOGEXIT("CSharedMemoryObject::PromoteSharedData\n");
}
/*++
Function:
CSharedMemoryObject::EnsureObjectIsShared
If this object is not yet in the shared domain allocate the necessary
shared memory structures for it and copy the object's data into those
structures
Parameters:
pthr -- thread data for the calling thread
--*/
PAL_ERROR
CSharedMemoryObject::EnsureObjectIsShared(
CPalThread *pthr
)
{
PAL_ERROR palError = NO_ERROR;
IDataLock *pDataLock = NULL;
SHMPTR shmObjData;
SHMObjData *psmod;
_ASSERTE(NULL != pthr);
ENTRY("CSharedMemoryObject::EnsureObjectIsShared"
"(this = %p, pthr = %p)\n",
this,
pthr
);
//
// Grab the shared memory lock and check if the object is already
// shared
//
SHMLock();
if (SharedObject == m_ObjectDomain)
{
goto EnsureObjectIsSharedExit;
}
//
// Grab the local shared data lock, if necessary
//
if (0 != m_pot->GetSharedDataSize())
{
m_sdlSharedData.AcquireLock(pthr, &pDataLock);
}
//
// Allocate the necessary shared data areas
//
palError = AllocateSharedDataItems(&shmObjData, &psmod);
if (NO_ERROR != palError)
{
goto EnsureObjectIsSharedExit;
}
//
// Promote the object's data and set the domain to shared
//
PromoteSharedData(shmObjData, psmod);
m_ObjectDomain = SharedObject;
EnsureObjectIsSharedExit:
if (NULL != pDataLock)
{
pDataLock->ReleaseLock(pthr, TRUE);
}
SHMRelease();
LOGEXIT("CSharedMemoryObject::EnsureObjectIsShared returns %d\n", palError);
return palError;
}
/*++
Function:
CSharedMemoryObject::CleanupForProcessShutdown
Cleanup routine called by the object manager when shutting down
Parameters:
pthr -- thread data for the calling thread
--*/
void
CSharedMemoryObject::CleanupForProcessShutdown(
CPalThread *pthr
)
{
bool fCleanupSharedState;
_ASSERTE(NULL != pthr);
ENTRY("CSharedMemoryObject::CleanupForProcessShutdown"
"(this = %p, pthr = %p)\n",
this,
pthr
);
fCleanupSharedState = DereferenceSharedData();
if (NULL != m_pot->GetObjectCleanupRoutine())
{
(*m_pot->GetObjectCleanupRoutine())(
pthr,
static_cast<IPalObject*>(this),
TRUE,
fCleanupSharedState
);
}
//
// 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();
InternalDelete(this);
pthr->ReleaseThreadReference();
LOGEXIT("CSharedMemoryObject::CleanupForProcessShutdown\n");
}
/*++
Function:
CSharedMemoryObject::AcquiteObjectDestructionLock
Acquires the lock that must be held when decrementing the object's
reference count (and, if the count drops to 0, while removing the
object from the object manager's lists).
Parameters:
pthr -- thread data for the calling thread
--*/
void
CSharedMemoryObject::AcquireObjectDestructionLock(
CPalThread *pthr
)
{
_ASSERTE(NULL != pthr);
ENTRY("CSharedMemoryObject::AcquireObjectDestructionLock"
"(this = %p, pthr = $p)\n",
this,
pthr
);
InternalEnterCriticalSection(pthr, m_pcsObjListLock);
LOGEXIT("CSharedMemoryObject::AcquireObjectDestructionLock\n");
}
/*++
Function:
CSharedMemoryObject::ReleaseObjectDestructionLock
Releases the lock acquired by AcquireObjectDestructionLock
Parameters:
pthr -- thread data for the calling thread
fDestructionPending -- if TRUE, the reference count for this
object has dropped to 0; the object will be destroyed after
this routine returns
--*/
bool
CSharedMemoryObject::ReleaseObjectDestructionLock(
CPalThread *pthr,
bool fDestructionPending
)
{
bool fCleanupSharedState = FALSE;
_ASSERTE(NULL != pthr);
ENTRY("CSharedMemoryObject::ReleaseObjectDestructionLock"
"(this = %p, pthr = %p, fDestructionPending = %d\n",
this,
pthr,
fDestructionPending
);
if (fDestructionPending)
{
RemoveEntryList(&m_le);
fCleanupSharedState = DereferenceSharedData();
}
InternalLeaveCriticalSection(pthr, m_pcsObjListLock);
LOGEXIT("CSharedMemoryObject::ReleaseObjectDestructionLock returns %d\n",
fCleanupSharedState
);
return fCleanupSharedState;
}
/*++
Function:
CSharedMemoryObject::DereferenceSharedData
Called to decrement the global refcount (i.e., the count of
the number of processes that have reference to the object) when
the local reference to the object is being destroyed.
Return value:
Returns TRUE if this process needs to clean up the object's shared
data (i.e., the global refcount has dropped to 0, or the object
is in the local domain)
--*/
bool
CSharedMemoryObject::DereferenceSharedData()
{
LONG fSharedDataAlreadDereferenced;
ENTRY("CSharedMemoryObject::DereferenceSharedData(this = %p)\n", this);
fSharedDataAlreadDereferenced = InterlockedExchange(
&m_fSharedDataDereferenced,
TRUE
);
if (!fSharedDataAlreadDereferenced)
{
if (SHMNULL != m_shmod)
{
SHMObjData *psmod;
SHMLock();
psmod = SHMPTR_TO_TYPED_PTR(SHMObjData, m_shmod);
_ASSERTE(NULL != psmod);
psmod->lProcessRefCount -= 1;
if (0 == psmod->lProcessRefCount)
{
//
// No other process is using this object, so remove
// it from the shared memory named object list (if it
// had been added to it). The final cleanup will happen
// in the object's destructor
//
m_fDeleteSharedData = TRUE;
if (psmod->fAddedToList)
{
//
// This object better have a name...
//
_ASSERTE(0 != psmod->dwNameLength);
if (SHMNULL != psmod->shmPrevObj)
{
SHMObjData *psmodPrevious = SHMPTR_TO_TYPED_PTR(SHMObjData, psmod->shmPrevObj);
_ASSERTE(NULL != psmodPrevious);
psmodPrevious->shmNextObj = psmod->shmNextObj;
}
else
{
//
// This object is the head of the shared memory named object
// list -- reset that pointer now
//
if (!SHMSetInfo(SIID_NAMED_OBJECTS, psmod->shmNextObj))
{
ASSERT("Failed to set shared named object list head");
}
}
if (SHMNULL != psmod->shmNextObj)
{
SHMObjData *psmodNext = SHMPTR_TO_TYPED_PTR(SHMObjData, psmod->shmNextObj);
_ASSERTE(NULL != psmodNext);
psmodNext->shmPrevObj = psmod->shmPrevObj;
}
}
#if _DEBUG
else
{
_ASSERTE(SHMNULL == psmod->shmPrevObj);
_ASSERTE(SHMNULL == psmod->shmNextObj);
}
#endif
}
SHMRelease();
}
else if (ProcessLocalObject == m_ObjectDomain)
{
//
// If the object is local the shared data needs to be
// deleted by definition
//
m_fDeleteSharedData = TRUE;
}
}
else
{
ASSERT("Multiple calls to DereferenceSharedData\n");
}
LOGEXIT("CSharedMemoryObject::DereferenceSharedData returns %d\n",
m_fDeleteSharedData
);
return m_fDeleteSharedData;
}
/*++
Function:
CSharedMemoryObject::~CSharedMemoryObject
Destructor; should only be called from ReleaseReference
--*/
CSharedMemoryObject::~CSharedMemoryObject()
{
ENTRY("CSharedMemoryObject::~CSharedMemoryObject(this = %p)\n", this);
if (!m_fSharedDataDereferenced)
{
ASSERT("DereferenceSharedData not called before object destructor -- delete called directly?\n");
DereferenceSharedData();
}
if (NULL != m_pvSharedData && ProcessLocalObject == m_ObjectDomain)
{
InternalFree(m_pvSharedData);
}
else if (SHMNULL != m_shmod && m_fDeleteSharedData)
{
FreeSharedDataAreas(m_shmod);
}
LOGEXIT("CSharedMemoryObject::~CSharedMemoryObject\n");
}
//
// C++ standard, 18.1.5 - offsetof requires a POD (plain old data) struct or
// union. Since offsetof is a macro, gcc doesn't actually check for improper
// use of offsetof, it keys off of the -> from NULL (which is also invalid for
// non-POD types by 18.1.5)
//
// As we have numerous examples of this behavior in our codebase,
// making an offsetof which doesn't use 0.
//
// PAL_safe_offsetof is a version of offsetof that protects against an
// overridden operator&
//
#define PAL_safe_offsetof(s,m) ((size_t)((ptrdiff_t)&(char&)(((s *)64)->m))-64)
/*++
Function:
CSharedMemoryObject::GetObjectFromListLink
Given a list link returns the object that contains it. Since m_le is
protected the caller cannot perform this computation directly
Parameters:
ple -- the list entry to obtain the object for
--*/
// static
CSharedMemoryObject*
CSharedMemoryObject::GetObjectFromListLink(PLIST_ENTRY ple)
{
CSharedMemoryObject *pshmo;
_ASSERTE(NULL != ple);
ENTRY("CSharedMemoryObject::GetObjectFromListLink(ple = %p)\n", ple);
//
// Ideally we'd use CONTAINING_RECORD here, but it uses offsetof (see above
// comment
//
pshmo = reinterpret_cast<CSharedMemoryObject*>(
reinterpret_cast<size_t>(ple) - PAL_safe_offsetof(CSharedMemoryObject, m_le)
);
_ASSERTE(ple == &pshmo->m_le);
LOGEXIT("CSharedMemoryObject::GetObjectFromListLink returns %p\n", pshmo);
return pshmo;
}
/*++
Function:
CSharedMemoryObject::GetSharedData
Provides the caller access to the object's shared 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 shared data
ppvProcssSharedData -- on success, receives a pointer to the shared data
--*/
PAL_ERROR
CSharedMemoryObject::GetSharedData(
CPalThread *pthr,
LockType eLockRequest,
IDataLock **ppDataLock, // OUT
void **ppvSharedData // OUT
)
{
IDataLock *pDataLock;
_ASSERTE(NULL != pthr);
_ASSERTE(ReadLock == eLockRequest || WriteLock == eLockRequest);
_ASSERTE(NULL != ppDataLock);
_ASSERTE(NULL != ppvSharedData);
ENTRY("CSharedMemoryObject::GetSharedData"
"(this = %p, pthr = %p, eLockRequest = %d, ppDataLock = %p,"
" ppvSharedData = %p)\n",
this,
pthr,
eLockRequest,
ppDataLock,
ppvSharedData
);
_ASSERTE(0 < m_pot->GetSharedDataSize());
if (ProcessLocalObject == m_ObjectDomain)
{
//
// We need to grab the local shared data lock and re-check
// the object's domain, as there's a chance the object might
// have been promoted after we made the above check but before
// we grabbed the lock
//
m_sdlSharedData.AcquireLock(pthr, &pDataLock);
if (SharedObject == m_ObjectDomain)
{
pDataLock->ReleaseLock(pthr, FALSE);
m_ssmlSharedData.AcquireLock(pthr, &pDataLock);
}
}
else
{
//
// A shared object can never transition back to local,
// so there's no need to recheck the domain on this path
//
m_ssmlSharedData.AcquireLock(pthr, &pDataLock);
}
*ppDataLock = pDataLock;
*ppvSharedData = m_pvSharedData;
LOGEXIT("CSharedMemoryObject::GetSharedData returns %d\n", NO_ERROR);
return NO_ERROR;
}
/*++
Function:
CSharedMemoryObject::GetSynchStateController
Obtain a synchronization state controller for this object. Should
never be called.
Parameters:
pthr -- thread data for calling thread
ppStateController -- on success, receives a pointer to the state controller
instance
--*/
PAL_ERROR
CSharedMemoryObject::GetSynchStateController(
CPalThread *pthr,
ISynchStateController **ppStateController // OUT
)
{
_ASSERTE(NULL != pthr);
_ASSERTE(NULL != ppStateController);
//
// This is not a waitable object!
//
ASSERT("Attempt to obtain a synch state controller on a non-waitable object\n");
return ERROR_INVALID_HANDLE;
}
/*++
Function:
CSharedMemoryObject::GetSynchWaitController
Obtain a synchronization wait controller for this object. Should
never be called.
Parameters:
pthr -- thread data for calling thread
ppWaitController -- on success, receives a pointer to the wait controller
instance
--*/
PAL_ERROR
CSharedMemoryObject::GetSynchWaitController(
CPalThread *pthr,
ISynchWaitController **ppWaitController // OUT
)
{
_ASSERTE(NULL != pthr);
_ASSERTE(NULL != ppWaitController);
//
// This is not a waitable object!!!
//
ASSERT("Attempt to obtain a synch wait controller on a non-waitable object\n");
return ERROR_INVALID_HANDLE;
}
/*++
Function:
CSharedMemoryObject::GetObjectDomain
Returns the object's domain (local or shared)
--*/
ObjectDomain
CSharedMemoryObject::GetObjectDomain(
void
)
{
TRACE("CSharedMemoryObject::GetObjectDomain(this = %p)\n", this);
LOGEXIT("CSharedMemoryObject::GetObjectDomain returns %d\n", m_ObjectDomain);
return m_ObjectDomain;
}
/*++
Function:
CSharedMemoryObject::GetObjectSynchData
Obtain the synchronization data for this object. Should
never be called.
Parameters:
ppvSynchData -- on success, receives a pointer to the object's synch data
--*/
PAL_ERROR
CSharedMemoryObject::GetObjectSynchData(
VOID **ppvSynchData // OUT
)
{
_ASSERTE(NULL != ppvSynchData);
//
// This is not a waitable object!!!
//
ASSERT("Attempt to obtain a synch data for a non-waitable object\n");
return ERROR_INVALID_HANDLE;
}
/*++
Function:
CSharedMemoryWaitableObject::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
CSharedMemoryWaitableObject::Initialize(
CPalThread *pthr,
CObjectAttributes *poa
)
{
PAL_ERROR palError = NO_ERROR;
_ASSERTE(NULL != pthr);
_ASSERTE(NULL != poa);
ENTRY("CSharedMemoryWaitableObject::Initialize"
"(this = %p, pthr = %p, poa = %p)\n",
this,
pthr,
poa
);
palError = CSharedMemoryObject::Initialize(pthr, poa);
if (NO_ERROR != palError)
{
goto InitializeExit;
}
//
// Sanity check the passed in object type
//
_ASSERTE(CObjectType::WaitableObject == m_pot->GetSynchronizationSupport());
palError = g_pSynchronizationManager->AllocateObjectSynchData(
m_pot,
m_ObjectDomain,
&m_pvSynchData
);
if (NO_ERROR == palError && SharedObject == m_ObjectDomain)
{
SHMObjData *pshmod = SHMPTR_TO_TYPED_PTR(SHMObjData, m_shmod);
_ASSERTE(NULL != pshmod);
pshmod->pvSynchData = m_pvSynchData;
}
InitializeExit:
LOGEXIT("CSharedMemoryWaitableObject::Initialize returns %d\n", palError);
return palError;
}
/*++
Function:
CSharedMemoryWaitableObject::EnsureObjectIsShared
If this object is not yet in the shared domain allocate the necessary
shared memory structures for it and copy the object's data into those
structures
Parameters:
pthr -- thread data for the calling thread
--*/
PAL_ERROR
CSharedMemoryWaitableObject::EnsureObjectIsShared(
CPalThread *pthr
)
{
PAL_ERROR palError = NO_ERROR;
IDataLock *pDataLock = NULL;
SHMPTR shmObjData = SHMNULL;
SHMObjData *psmod;
VOID *pvSharedSynchData;
_ASSERTE(NULL != pthr);
ENTRY("CSharedMemoryWaitableObject::EnsureObjectIsShared"
"(this = %p, pthr = %p)\n",
this,
pthr
);
//
// First, grab the process synchronization lock and check
// if the object is already shared
//
g_pSynchronizationManager->AcquireProcessLock(pthr);
if (SharedObject == m_ObjectDomain)
{
goto EnsureObjectIsSharedExitNoSHMLockRelease;
}
//
// Grab the necessary locks
//
SHMLock();
if (0 != m_pot->GetSharedDataSize())
{
m_sdlSharedData.AcquireLock(pthr, &pDataLock);
}
//
// Allocate the necessary shared data areas
//
palError = AllocateSharedDataItems(&shmObjData, &psmod);
if (NO_ERROR != palError)
{
goto EnsureObjectIsSharedExit;
}
//
// Promote the object's synchronization data
//
palError = g_pSynchronizationManager->PromoteObjectSynchData(
pthr,
m_pvSynchData,
&pvSharedSynchData
);
if (NO_ERROR != palError)
{
goto EnsureObjectIsSharedExit;
}
m_pvSynchData = pvSharedSynchData;
psmod->pvSynchData = pvSharedSynchData;
//
// Promote the object's data and set the domain to shared
//
PromoteSharedData(shmObjData, psmod);
m_ObjectDomain = SharedObject;
EnsureObjectIsSharedExit:
if (NULL != pDataLock)
{
pDataLock->ReleaseLock(pthr, TRUE);
}
SHMRelease();
EnsureObjectIsSharedExitNoSHMLockRelease:
g_pSynchronizationManager->ReleaseProcessLock(pthr);
if (NO_ERROR != palError && SHMNULL != shmObjData)
{
//
// Since shmObjdData is local to this function there's no
// need to continue to hold the promotion locks when
// freeing the allocated data on error
//
FreeSharedDataAreas(shmObjData);
}
LOGEXIT("CSharedMemoryWaitableObject::EnsureObjectIsShared returns %d\n",
palError
);
return palError;
}
/*++
Function:
CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject
Destructor; should only be called from ReleaseReference
--*/
CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject()
{
ENTRY("CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject"
"(this = %p)\n",
this
);
if (!m_fSharedDataDereferenced)
{
ASSERT("DereferenceSharedData not called before object destructor -- delete called directly?\n");
DereferenceSharedData();
}
if (NULL != m_pvSynchData && m_fDeleteSharedData)
{
g_pSynchronizationManager->FreeObjectSynchData(
m_pot,
m_ObjectDomain,
m_pvSynchData
);
}
LOGEXIT("CSharedMemoryWaitableObject::~CSharedMemoryWaitableObject\n");
}
/*++
Function:
CSharedMemoryWaitableObject::GetSynchStateController
Obtain a synchronization state controller for this object.
Parameters:
pthr -- thread data for calling thread
ppStateController -- on success, receives a pointer to the state controller
instance
--*/
PAL_ERROR
CSharedMemoryWaitableObject::GetSynchStateController(
CPalThread *pthr, // IN, OPTIONAL
ISynchStateController **ppStateController // OUT
)
{
PAL_ERROR palError = NO_ERROR;
_ASSERTE(NULL != pthr);
_ASSERTE(NULL != ppStateController);
ENTRY("CSharedMemoryWaitableObject::GetSynchStateController"
"(this = %p, pthr = %p, ppStateController = %p",
this,
pthr,
ppStateController
);
//
// We need to grab the local synch lock before creating the controller
// (otherwise we could get promoted after passing in our parameters)
//
g_pSynchronizationManager->AcquireProcessLock(pthr);
palError = g_pSynchronizationManager->CreateSynchStateController(
pthr,
m_pot,
m_pvSynchData,
m_ObjectDomain,
ppStateController
);
g_pSynchronizationManager->ReleaseProcessLock(pthr);
LOGEXIT("CSharedMemoryWaitableObject::GetSynchStateController returns %d\n",
palError
);
return palError;
}
/*++
Function:
CSharedMemoryWaitableObject::GetSynchWaitController
Obtain a synchronization wait controller for this object.
Parameters:
pthr -- thread data for calling thread
ppWaitController -- on success, receives a pointer to the wait controller
instance
--*/
PAL_ERROR
CSharedMemoryWaitableObject::GetSynchWaitController(
CPalThread *pthr, // IN, OPTIONAL
ISynchWaitController **ppWaitController // OUT
)
{
PAL_ERROR palError = NO_ERROR;
_ASSERTE(NULL != pthr);
_ASSERTE(NULL != ppWaitController);
ENTRY("CSharedMemoryWaitableObject::GetSynchWaitController"
"(this = %p, pthr = %p, ppWaitController = %p",
this,
pthr,
ppWaitController
);
//
// We need to grab the local synch lock before creating the controller
// (otherwise we could get promoted after passing in our parameters)
//
g_pSynchronizationManager->AcquireProcessLock(pthr);
palError = g_pSynchronizationManager->CreateSynchWaitController(
pthr,
m_pot,
m_pvSynchData,
m_ObjectDomain,
ppWaitController
);
g_pSynchronizationManager->ReleaseProcessLock(pthr);
LOGEXIT("CSharedMemoryWaitableObject::GetSynchWaitController returns %d\n",
palError
);
return palError;
}
/*++
Function:
CSharedMemoryWaitableObject::GetObjectSynchData
Obtain the synchronization data for this object. This method should only
be called by the synchronization manager
Parameters:
ppvSynchData -- on success, receives a pointer to the object's synch data
--*/
PAL_ERROR
CSharedMemoryWaitableObject::GetObjectSynchData(
VOID **ppvSynchData // OUT
)
{
_ASSERTE(NULL != ppvSynchData);
ENTRY("CSharedMemoryWaitableObject::GetObjectSynchData"
"(this = %p, ppvSynchData = %p)\n",
this,
ppvSynchData
);
*ppvSynchData = m_pvSynchData;
LOGEXIT("CSharedMemoryWaitableObject::GetObjectSynchData returns %d\n",
NO_ERROR
);
return NO_ERROR;
}