| // |
| // 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; |
| } |
| |