| // |
| // Copyright (c) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. |
| // |
| |
| /*++ |
| |
| |
| |
| Module Name: |
| |
| synchcontrollers.cpp |
| |
| Abstract: |
| Implementation of Synchronization Controllers and related objects |
| |
| |
| |
| --*/ |
| |
| #include "synchmanager.hpp" |
| |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <sched.h> |
| #include <errno.h> |
| #include <limits.h> |
| |
| namespace CorUnix |
| { |
| #ifdef SYNCH_STATISTICS |
| LONG g_rglStatWaitCount[ObjectTypeIdCount] = { 0 }; |
| LONG g_rglStatContentionCount[ObjectTypeIdCount] = { 0 }; |
| #endif // SYNCH_STATISTICS |
| //////////////////////////// |
| // // |
| // CSynchControllerBase // |
| // // |
| //////////////////////////// |
| |
| /*++ |
| Method: |
| CSynchControllerBase::Init |
| |
| Initializes a generic controller |
| --*/ |
| PAL_ERROR CSynchControllerBase::Init( |
| CPalThread * pthrCurrent, |
| ControllerType ctCtrlrType, |
| ObjectDomain odObjectDomain, |
| CObjectType *potObjectType, |
| CSynchData * psdSynchData, |
| WaitDomain wdWaitDomain) |
| { |
| VALIDATEOBJECT(psdSynchData); |
| |
| _ASSERTE(InternalGetCurrentThread() == pthrCurrent); |
| |
| // Initialize internal controller data |
| m_pthrOwner = pthrCurrent; |
| m_ctCtrlrType = ctCtrlrType; |
| m_odObjectDomain = odObjectDomain; |
| m_potObjectType = potObjectType; |
| m_psdSynchData = psdSynchData; |
| m_wdWaitDomain = wdWaitDomain; |
| |
| // Add reference to target synch data |
| m_psdSynchData->AddRef(); |
| |
| // Acquire lock implied by the controller |
| CPalSynchronizationManager::AcquireLocalSynchLock(m_pthrOwner); |
| if (LocalWait != m_wdWaitDomain) |
| { |
| CPalSynchronizationManager::AcquireSharedSynchLock(m_pthrOwner); |
| } |
| |
| return NO_ERROR; |
| } |
| |
| /*++ |
| Method: |
| CSynchControllerBase::Release |
| |
| Releases a generic controller a return it to the appropriate cache |
| --*/ |
| void CSynchControllerBase::Release() |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| #ifdef _DEBUG |
| ThreadWaitInfo * ptwiWaitInfo = |
| CPalSynchronizationManager::GetThreadWaitInfo(m_pthrOwner); |
| #endif // _DEBUG |
| |
| CPalSynchronizationManager * pSynchManager = |
| CPalSynchronizationManager::GetInstance(); |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| _ASSERTE(ptwiWaitInfo->pthrOwner == m_pthrOwner); |
| |
| // Release reference to target synch data |
| m_psdSynchData->Release(m_pthrOwner); |
| |
| // Release lock implied by the controller |
| if (LocalWait != m_wdWaitDomain) |
| { |
| CPalSynchronizationManager::ReleaseSharedSynchLock(m_pthrOwner); |
| } |
| CPalSynchronizationManager::ReleaseLocalSynchLock(m_pthrOwner); |
| |
| // Return controller to the appropriate cache |
| if (WaitController == m_ctCtrlrType) |
| { |
| // The cast here must be static_cast and not reinterpet_cast. |
| // In fact in general static_cast<CSynchWaitController*>(this) is |
| // equal to this-sizeof(void*), given that CSynchWaitController |
| // has a virtual table, while CSynchControllerBase doesn't. |
| pSynchManager->CacheAddWaitCtrlr(m_pthrOwner, |
| static_cast<CSynchWaitController*>(this)); |
| } |
| else |
| { |
| // The cast here must be static_cast and not reinterpet_cast |
| pSynchManager->CacheAddStateCtrlr(m_pthrOwner, |
| static_cast<CSynchStateController*>(this)); |
| } |
| } |
| |
| //////////////////////////// |
| // // |
| // CSynchWaitController // |
| // // |
| //////////////////////////// |
| |
| /*++ |
| Method: |
| CSynchWaitController::CanThreadWaitWithoutBlocking |
| |
| Returns whether or not the thread owning this controller can |
| wait on the target object without blocking (i.e. the objet is |
| signaled) |
| --*/ |
| PAL_ERROR CSynchWaitController::CanThreadWaitWithoutBlocking( |
| bool * pfCanWaitWithoutBlocking, |
| bool * pfAbandoned) |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| bool fRetVal = false; |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| _ASSERTE(NULL != pfCanWaitWithoutBlocking); |
| _ASSERTE(NULL != pfAbandoned); |
| |
| fRetVal = m_psdSynchData->CanWaiterWaitWithoutBlocking(m_pthrOwner, pfAbandoned); |
| |
| if(!fRetVal && otiProcess == m_psdSynchData->GetObjectTypeId()) |
| { |
| // Note: if the target object is a process, here we need to check |
| // whether or not it has already exited. In fact, since currently |
| // we do not monitor a process status as long as there is no |
| // thread waiting on it, in general if the process already exited |
| // the process object is likely not to be signaled yet, therefore |
| // the above CanWaiterWaitWithoutBlocking call probably returned |
| // false, and, without the check below, that would cause the |
| // current thread to eventually go to sleep for a short time |
| // (until the worker thread notifies that the waited process has |
| // indeed exited), while it would not be necessary. |
| // As side effect that would cause a WaitForSingleObject with zero |
| // timeout to always return WAIT_TIMEOUT, even though the target |
| // process already exited. WaitForSingleObject with zero timeout |
| // is a common way to probe whether or not a process has already |
| // exited, and it is supposed to return WAIT_OBJECT_0 if the |
| // process exited, and WAIT_TIMEOUT if it is still active. |
| // In order to support this feature we need to check at this time |
| // whether or not the process has already exited. |
| |
| CProcProcessLocalData * pProcLocalData = GetProcessLocalData(); |
| DWORD dwExitCode = 0; |
| bool fIsActualExitCode = false; |
| |
| _ASSERT_MSG(NULL != pProcLocalData, |
| "Process synch data pointer is missing\n"); |
| |
| if (NULL != pProcLocalData && |
| CPalSynchronizationManager::HasProcessExited(pProcLocalData->dwProcessId, |
| &dwExitCode, |
| &fIsActualExitCode)) |
| { |
| TRACE("Process pid=%u exited with %s exitcode=%u\n", |
| pProcLocalData->dwProcessId, |
| fIsActualExitCode ? "actual" : "guessed", |
| dwExitCode); |
| |
| // Store the exit code in the process local data |
| if (fIsActualExitCode) |
| { |
| pProcLocalData->dwExitCode = dwExitCode; |
| } |
| |
| // Set process status to PS_DONE |
| pProcLocalData->ps = PS_DONE; |
| |
| // Set signal count |
| m_psdSynchData->SetSignalCount(1); |
| |
| // Releasing all local waiters |
| // (see comments in DoMonitorProcesses) |
| m_psdSynchData->ReleaseAllLocalWaiters(m_pthrOwner); |
| |
| fRetVal = true; |
| } |
| } |
| |
| *pfCanWaitWithoutBlocking = fRetVal; |
| return NO_ERROR; |
| } |
| |
| /*++ |
| Method: |
| CSynchWaitController::ReleaseWaitingThreadWithoutBlocking |
| |
| Performs all the steps needed to be done by the controller's owner |
| thread in order to wait on the target object without blocking |
| (e.g. modifying the object signal count accordingly with its |
| thread release semantics) |
| This method should be called only after having received positive |
| response from CanThreadWaitWithoutBlocking called on the same |
| controller. |
| --*/ |
| PAL_ERROR CSynchWaitController::ReleaseWaitingThreadWithoutBlocking() |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| PAL_ERROR palErr = NO_ERROR; |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| |
| palErr = m_psdSynchData->ReleaseWaiterWithoutBlocking(m_pthrOwner, m_pthrOwner); |
| |
| #ifdef SYNCH_STATISTICS |
| if (NO_ERROR == palErr) |
| { |
| m_psdSynchData->IncrementStatWaitCount(); |
| } |
| #endif |
| return palErr; |
| } |
| |
| /*++ |
| Method: |
| CSynchWaitController::RegisterWaitingThread |
| |
| Registers the controller's owner thread for waiting on the target |
| object |
| --*/ |
| PAL_ERROR CSynchWaitController::RegisterWaitingThread( |
| WaitType wtWaitType, |
| DWORD dwIndex, |
| bool fAlertable) |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| PAL_ERROR palErr = NO_ERROR; |
| WaitingThreadsListNode * pwtlnNewNode = NULL; |
| SharedID shridNewNode = NULLSharedID; |
| ThreadWaitInfo * ptwiWaitInfo; |
| DWORD * pdwWaitState; |
| bool fSharedObject = (SharedObject == m_odObjectDomain); |
| bool fEarlyDeath = false; |
| bool fSynchDataRefd = false; |
| CPalSynchronizationManager * pSynchManager = |
| CPalSynchronizationManager::GetInstance(); |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| |
| ptwiWaitInfo = CPalSynchronizationManager::GetThreadWaitInfo( |
| m_pthrOwner); |
| |
| _ASSERTE(ptwiWaitInfo->pthrOwner == m_pthrOwner); |
| |
| pdwWaitState = SharedIDToTypePointer(DWORD, |
| m_pthrOwner->synchronizationInfo.m_shridWaitAwakened); |
| |
| if (fSharedObject) |
| { |
| shridNewNode = pSynchManager->CacheGetSharedWTListNode(m_pthrOwner); |
| pwtlnNewNode = SharedIDToTypePointer(WaitingThreadsListNode, shridNewNode); |
| } |
| else |
| { |
| pwtlnNewNode = pSynchManager->CacheGetLocalWTListNode(m_pthrOwner); |
| } |
| |
| if (!pwtlnNewNode) |
| { |
| if (fSharedObject && (NULLSharedID != shridNewNode)) |
| { |
| ASSERT("Bad Shared Memory ptr %p\n", shridNewNode); |
| palErr = ERROR_INTERNAL_ERROR; |
| } |
| else |
| { |
| ERROR("Out of memory\n"); |
| palErr = ERROR_NOT_ENOUGH_MEMORY; |
| } |
| goto RWT_exit; |
| } |
| |
| if (ptwiWaitInfo->lObjCount >= MAXIMUM_WAIT_OBJECTS) |
| { |
| ASSERT("Too many objects"); |
| palErr = ERROR_INTERNAL_ERROR; |
| goto RWT_exit; |
| } |
| |
| if (0 == ptwiWaitInfo->lObjCount) |
| { |
| ptwiWaitInfo->wtWaitType = wtWaitType; |
| ptwiWaitInfo->wdWaitDomain = m_wdWaitDomain; |
| } |
| else |
| { |
| _ASSERT_MSG(wtWaitType == ptwiWaitInfo->wtWaitType, |
| "Conflicting wait types in wait registration\n"); |
| |
| if (m_wdWaitDomain != ptwiWaitInfo->wdWaitDomain) |
| { |
| ptwiWaitInfo->wdWaitDomain = MixedWait; |
| } |
| } |
| |
| pwtlnNewNode->shridSHRThis = NULLSharedID; |
| pwtlnNewNode->ptwiWaitInfo = ptwiWaitInfo; |
| pwtlnNewNode->dwObjIndex = dwIndex; |
| pwtlnNewNode->dwProcessId = gPID; |
| pwtlnNewNode->dwThreadId = m_pthrOwner->GetThreadId(); |
| pwtlnNewNode->dwFlags = (MultipleObjectsWaitAll == wtWaitType) ? |
| WTLN_FLAG_WAIT_ALL : 0; |
| pwtlnNewNode->shridWaitingState = m_pthrOwner->synchronizationInfo.m_shridWaitAwakened; |
| if (fSharedObject) |
| { |
| pwtlnNewNode->dwFlags |= WTLN_FLAG_OWNER_OBJECT_IS_SHARED; |
| pwtlnNewNode->shridSHRThis = shridNewNode; |
| pwtlnNewNode->ptrOwnerObjSynchData.shrid = m_psdSynchData->GetSharedThis(); |
| } |
| else |
| { |
| pwtlnNewNode->ptrOwnerObjSynchData.ptr = m_psdSynchData; |
| } |
| |
| // AddRef the synch data (will be released in UnregisterWait) |
| m_psdSynchData->AddRef(); |
| fSynchDataRefd = true; |
| |
| ptwiWaitInfo->rgpWTLNodes[ptwiWaitInfo->lObjCount] = pwtlnNewNode; |
| |
| if(otiProcess == m_psdSynchData->GetObjectTypeId()) |
| { |
| CProcProcessLocalData * pProcLocalData = GetProcessLocalData(); |
| |
| if (NULL == pProcLocalData) |
| { |
| // Process local data pointer not set in the controller. |
| // This pointer is set in CSynchWaitController only when the |
| // wait controller for the object is created by calling |
| // GetSynchWaitControllersForObjects |
| ASSERT("Process synch data pointer is missing\n"); |
| palErr = ERROR_INTERNAL_ERROR; |
| goto RWT_exit; |
| } |
| |
| palErr = pSynchManager->RegisterProcessForMonitoring(m_pthrOwner, |
| m_psdSynchData, |
| pProcLocalData); |
| if (NO_ERROR != palErr) |
| { |
| goto RWT_exit; |
| } |
| } |
| |
| if (0 == ptwiWaitInfo->lObjCount) |
| { |
| DWORD dwWaitState; |
| |
| // Setting the thread in wait state |
| dwWaitState = (DWORD)(fAlertable ? TWS_ALERTABLE: TWS_WAITING); |
| |
| TRACE("Switching my wait state [%p] from TWS_ACTIVE to %u \n", |
| pdwWaitState, dwWaitState); |
| |
| dwWaitState = InterlockedCompareExchange( |
| (LONG *)pdwWaitState, (LONG)dwWaitState, TWS_ACTIVE); |
| if ((DWORD)TWS_ACTIVE != dwWaitState) |
| { |
| if ((DWORD)TWS_EARLYDEATH == dwWaitState) |
| { |
| // Process is terminating, this thread will soon be |
| // suspended (by SuspendOtherThreads). |
| WARN("Thread is about to get suspended by " |
| "TerminateProcess\n"); |
| |
| fEarlyDeath = true; |
| palErr = WAIT_FAILED; |
| } |
| else |
| { |
| ASSERT("Unexpected thread wait state %d\n", dwWaitState); |
| palErr = ERROR_INTERNAL_ERROR; |
| } |
| goto RWT_exit; |
| } |
| } |
| |
| // Add new node to queue |
| if (fSharedObject) |
| { |
| m_psdSynchData->SharedWaiterEnqueue(shridNewNode); |
| ptwiWaitInfo->lSharedObjCount += 1; |
| } |
| else |
| { |
| m_psdSynchData->WaiterEnqueue(pwtlnNewNode); |
| } |
| |
| // Succeeded: update object count |
| ptwiWaitInfo->lObjCount++; |
| |
| RWT_exit: |
| if (palErr != NO_ERROR) |
| { |
| // Unregister any partial wait registration |
| pSynchManager->UnRegisterWait(m_pthrOwner, ptwiWaitInfo, fSharedObject); |
| |
| if (fSynchDataRefd) |
| { |
| m_psdSynchData->Release(m_pthrOwner); |
| } |
| if ((fSharedObject) && (NULLSharedID != shridNewNode)) |
| { |
| pSynchManager->CacheAddSharedWTListNode(m_pthrOwner, shridNewNode); |
| } |
| else if (NULL != pwtlnNewNode) |
| { |
| pSynchManager->CacheAddLocalWTListNode(m_pthrOwner, pwtlnNewNode); |
| } |
| |
| if (fEarlyDeath) |
| { |
| // Early death detected, i.e. the process is about to exit. |
| // We need to completely release the synch lock(s) before |
| // going to sleep |
| LONG lLocalSynchLockCount; |
| LONG lSharedSynchLockCount; |
| |
| lSharedSynchLockCount = CPalSynchronizationManager::ResetSharedSynchLock(m_pthrOwner); |
| lLocalSynchLockCount = CPalSynchronizationManager::ResetLocalSynchLock(m_pthrOwner); |
| |
| _ASSERTE(0 < lLocalSynchLockCount); |
| |
| // Sleep for ever |
| CPalSynchronizationManager::ThreadPrepareForShutdown(); |
| } |
| } |
| #ifdef SYNCH_STATISTICS |
| else |
| { |
| m_psdSynchData->IncrementStatWaitCount(); |
| m_psdSynchData->IncrementStatContentionCount(); |
| } |
| #endif |
| return palErr; |
| } |
| |
| /*++ |
| Method: |
| CSynchWaitController::ReleaseController |
| |
| Releases the current controller |
| --*/ |
| void CSynchWaitController::ReleaseController() |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| |
| Release(); |
| } |
| |
| /*++ |
| Method: |
| CSynchWaitController::GetProcessLocalData |
| |
| Accessor Get method for process local data of the target object |
| --*/ |
| CProcProcessLocalData * CSynchWaitController::GetProcessLocalData() |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| _ASSERT_MSG(NULL != m_pProcLocalData, |
| "Pointer to process local data not yet initialized\n"); |
| |
| return m_pProcLocalData; |
| } |
| |
| /*++ |
| Method: |
| CSynchWaitController::SetProcessLocalData |
| |
| Accessor Set method for process local data of the target object |
| --*/ |
| void CSynchWaitController::SetProcessLocalData(CProcProcessLocalData * pProcLocalData) |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| m_pProcLocalData = pProcLocalData; |
| } |
| |
| ///////////////////////////// |
| // // |
| // CSynchStateController // |
| // // |
| ///////////////////////////// |
| |
| /*++ |
| Method: |
| CSynchStateController::GetSignalCount |
| |
| Returns the current signal count of the target object |
| --*/ |
| PAL_ERROR CSynchStateController::GetSignalCount(LONG *plSignalCount) |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| PAL_ERROR palErr = NO_ERROR; |
| LONG lCount = m_psdSynchData->GetSignalCount(); |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| _ASSERTE(NULL != plSignalCount); |
| _ASSERT_MSG(0 <= lCount, |
| "Internal error: negative signal count [signal count=%d]", |
| lCount); |
| |
| *plSignalCount = lCount; |
| return palErr; |
| } |
| |
| /*++ |
| Method: |
| CSynchStateController::SetSignalCount |
| |
| Sets the signal count of the target object, possibly triggering |
| waiting threads awakening. |
| --*/ |
| PAL_ERROR CSynchStateController::SetSignalCount(LONG lNewCount) |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| _ASSERTE(lNewCount >= 0); |
| |
| m_psdSynchData->Signal(m_pthrOwner, lNewCount, false); |
| |
| return NO_ERROR; |
| } |
| |
| /*++ |
| Method: |
| CSynchStateController::IncrementSignalCount |
| |
| Increments the signal count of the target object, possibly triggering |
| waiting threads awakening. |
| --*/ |
| PAL_ERROR CSynchStateController::IncrementSignalCount( |
| LONG lAmountToIncrement) |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| _ASSERTE(lAmountToIncrement > 0); |
| |
| LONG lOldCount = m_psdSynchData->GetSignalCount(); |
| LONG lNewCount = lOldCount + lAmountToIncrement; |
| |
| _ASSERT_MSG(lNewCount > lOldCount, |
| "Signal count increment %d would make current signal count %d to " |
| "wrap around\n", lAmountToIncrement, lOldCount); |
| |
| m_psdSynchData->Signal(m_pthrOwner, lNewCount, false); |
| |
| return NO_ERROR; |
| } |
| |
| /*++ |
| Method: |
| CSynchStateController::DecrementSignalCount |
| |
| Decrements the signal count of the target object. |
| --*/ |
| PAL_ERROR CSynchStateController::DecrementSignalCount( |
| LONG lAmountToDecrement) |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| _ASSERTE(lAmountToDecrement > 0); |
| |
| PAL_ERROR palErr = NO_ERROR; |
| LONG lCount = m_psdSynchData->GetSignalCount(); |
| _ASSERTE(lAmountToDecrement <= lCount); |
| |
| m_psdSynchData->SetSignalCount(lCount - lAmountToDecrement); |
| |
| return palErr; |
| } |
| |
| /*++ |
| Method: |
| CSynchStateController::SetOwner |
| |
| Sets the owner of the target object and initializes the ownership |
| count to 1 (for objects with tracked ownership). |
| --*/ |
| PAL_ERROR CSynchStateController::SetOwner(CPalThread * pNewOwningThread) |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| PAL_ERROR palErr = NO_ERROR; |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| _ASSERTE(NULL != pNewOwningThread); |
| _ASSERT_MSG(CObjectType::OwnershipTracked == |
| m_potObjectType->GetOwnershipSemantics(), |
| "SetOwner called on an object without OwnershipTracked " |
| "semantics\n"); |
| |
| if (0 != m_psdSynchData->GetOwnershipCount()) |
| { |
| ASSERT("Ownership count should be zero at this time\n"); |
| palErr = ERROR_INTERNAL_ERROR; |
| goto SO_exit; |
| } |
| |
| palErr = m_psdSynchData->AssignOwnershipToThread(m_pthrOwner, |
| pNewOwningThread); |
| |
| _ASSERT_MSG(0 == m_psdSynchData->GetOwnershipCount() || |
| 0 == m_psdSynchData->GetSignalCount(), |
| "Conflicting values for SignalCount [%d] and " |
| "OwnershipCount [%d]\n", |
| m_psdSynchData->GetOwnershipCount(), |
| m_psdSynchData->GetSignalCount()); |
| |
| SO_exit: |
| return palErr; |
| } |
| |
| /*++ |
| Method: |
| CSynchStateController::DecrementOwnershipCount |
| |
| Decrements the ownership count of the target object possibly triggering |
| waiting threads awakening (for objects with tracked ownership). |
| --*/ |
| PAL_ERROR CSynchStateController::DecrementOwnershipCount() |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| PAL_ERROR palErr = NO_ERROR; |
| LONG lOwnershipCount = m_psdSynchData->GetOwnershipCount(); |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| _ASSERT_MSG(CObjectType::OwnershipTracked == |
| m_potObjectType->GetOwnershipSemantics(), |
| "Trying to decrement ownership count on an object with " |
| "ownership semantics other than OwnershipTracked\n"); |
| _ASSERT_MSG(0 <= lOwnershipCount, |
| "Operation would make ownership count negative - object " |
| "should be owned at this time [ownership count=%d]\n", |
| lOwnershipCount); |
| |
| if ( (1 > lOwnershipCount) || |
| (m_psdSynchData->GetOwnerProcessID() != gPID) || |
| (m_psdSynchData->GetOwnerThread() != m_pthrOwner) ) |
| { |
| palErr = ERROR_NOT_OWNER; |
| goto DOC_exit; |
| } |
| |
| lOwnershipCount--; |
| m_psdSynchData->SetOwnershipCount(lOwnershipCount); |
| |
| if (0 == lOwnershipCount) |
| { |
| CPalSynchronizationManager * pSynchManager = |
| CPalSynchronizationManager::GetInstance(); |
| OwnedObjectsListNode * pooln = |
| m_psdSynchData->GetOwnershipListNode(); |
| |
| _ASSERT_MSG(NULL != pooln, |
| "Null ownership node pointer in SynchData with ownership " |
| "semantics\n"); |
| _ASSERT_MSG(m_psdSynchData == pooln->pPalObjSynchData, |
| "Corrupted ownership node\n"); |
| |
| // Object has been released |
| // Remove it from list of owned objs for current thread |
| m_pthrOwner->synchronizationInfo.RemoveObjectFromOwnedList(pooln); |
| |
| // Release SynchData reference count implied by the ownership |
| // list node |
| m_psdSynchData->Release(m_pthrOwner); |
| |
| // Return node to the cache |
| pSynchManager->CacheAddOwnedObjsListNode(m_pthrOwner, pooln); |
| |
| // Reset ownership |
| m_psdSynchData->ResetOwnership(); |
| |
| // Signal it and trigger waiter thread awakening |
| m_psdSynchData->Signal(m_pthrOwner, 1, false); |
| } |
| |
| DOC_exit: |
| return palErr; |
| } |
| |
| /*++ |
| Method: |
| CSynchStateController::ReleaseController |
| |
| Releases the controller. |
| --*/ |
| void CSynchStateController::ReleaseController(void) |
| { |
| VALIDATEOBJECT(m_psdSynchData); |
| |
| _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); |
| |
| Release(); |
| } |
| |
| ////////////////// |
| // // |
| // CSynchData // |
| // // |
| ////////////////// |
| |
| /*++ |
| Method: |
| CSynchData::Release |
| |
| Decremnt the reference count of the target synchdata and retrurns |
| it to the appropriate cache if the reference count reaches zero. |
| --*/ |
| LONG CSynchData::Release(CPalThread * pthrCurrent) |
| { |
| VALIDATEOBJECT(this); |
| |
| LONG lCount = InterlockedDecrement(&m_lRefCount); |
| |
| _ASSERT_MSG(0 <= lCount, |
| "CSynchData %p with negative reference count [%d]\n", |
| this, lCount); |
| |
| if (0 == lCount) |
| { |
| CPalSynchronizationManager * pSynchManager = |
| CPalSynchronizationManager::GetInstance(); |
| bool fSharedObject = (SharedObject == m_odObjectDomain); |
| |
| _ASSERT_MSG((fSharedObject && (NULLSharedID == m_ptrWTLHead.shrid)) || |
| (!fSharedObject && (NULL == m_ptrWTLHead.ptr)), |
| "Final Release on CSynchData with threads still in " |
| "the waiting list\n"); |
| |
| TRACE("Disposing %s waitable object with SynchData @ " |
| "{shrid=%p, p=%p}\n", |
| (SharedObject == m_odObjectDomain) ? "shared" : "local", |
| (PVOID)m_shridThis, this); |
| |
| |
| #ifdef SYNCH_STATISTICS |
| LONG lStatWaitCount = GetStatWaitCount(); |
| LONG lStatContentionCount = GetStatContentionCount(); |
| LONG lCount, lNewCount; |
| |
| TRACE("Statistical data for SynchData of otiType=%u @ %p: WaitCount=%d " |
| "ContentionCount=%d\n", m_otiObjectTypeId, this, lStatWaitCount, |
| lStatContentionCount); |
| |
| do { |
| lCount = g_rglStatWaitCount[m_otiObjectTypeId]; |
| lNewCount = lCount + lStatWaitCount; |
| lNewCount = InterlockedCompareExchange(&(g_rglStatWaitCount[m_otiObjectTypeId]), |
| lNewCount, lCount); |
| } while (lCount != lNewCount); |
| |
| lStatWaitCount = lNewCount; |
| |
| do { |
| lCount = g_rglStatContentionCount[m_otiObjectTypeId]; |
| lNewCount = lCount + lStatContentionCount; |
| lNewCount = InterlockedCompareExchange(&(g_rglStatContentionCount[m_otiObjectTypeId]), |
| lNewCount, lCount); |
| } while (lCount != lNewCount); |
| |
| lStatContentionCount = lNewCount; |
| |
| TRACE("Total current statistical data for otiType=%u objects: WaitCount=%d " |
| "ContentionCount=%d\n", m_otiObjectTypeId, lStatWaitCount, |
| lStatContentionCount); |
| #endif // SYNCH_STATISTICS |
| |
| if (fSharedObject) |
| { |
| pSynchManager->CacheAddSharedSynchData(pthrCurrent, m_shridThis); |
| } |
| else |
| { |
| pSynchManager->CacheAddLocalSynchData(pthrCurrent, this); |
| } |
| } |
| |
| return lCount; |
| } |
| |
| /*++ |
| Method: |
| CSynchData::ReleaseWaiterWithoutBlocking |
| |
| Performs all the steps needed to be done by the target thread in order |
| to wait without blocking on the object associated with the current |
| SynchData (e.g. modifying the object signal count accordingly with its |
| thread release semantics) |
| |
| Note: this method must be called while holding the appropriate |
| synchronization locks (the local process synch lock if the target |
| object is local, both local and shared one if the object is shared). |
| --*/ |
| PAL_ERROR CSynchData::ReleaseWaiterWithoutBlocking( |
| CPalThread * pthrCurrent, |
| CPalThread * pthrTarget) |
| { |
| VALIDATEOBJECT(this); |
| |
| PAL_ERROR palErr = NO_ERROR; |
| CObjectType * potObjectType = GetObjectType(); |
| #ifdef _DEBUG |
| CObjectType::SignalingSemantics ssSignalingSemantics = |
| potObjectType->GetSignalingSemantics(); |
| #endif // _DEBUG |
| CObjectType::OwnershipSemantics osOwnershipSemantics = |
| potObjectType->GetOwnershipSemantics(); |
| CObjectType::ThreadReleaseSemantics trsThreadReleaseSemantics = |
| potObjectType->GetThreadReleaseSemantics(); |
| bool fReenteringObjWithOwnership = false; |
| |
| _ASSERT_MSG(CObjectType::SignalingNotApplicable != ssSignalingSemantics, |
| "Signaling not applicable"); |
| _ASSERT_MSG(CObjectType::ThreadReleaseNotApplicable != |
| trsThreadReleaseSemantics, |
| "Thread releasing not applicable"); |
| _ASSERT_MSG(CObjectType::SingleTransitionObject != ssSignalingSemantics || |
| (CObjectType::ThreadReleaseHasNoSideEffects == |
| trsThreadReleaseSemantics && |
| CObjectType::NoOwner == osOwnershipSemantics), |
| "Conflicting object synchronization attributes " |
| "[SignalingSemantics=%u OwnershipSemantics=%u " |
| "ThreadReleaseSemantics=%u]\n", ssSignalingSemantics, |
| osOwnershipSemantics, trsThreadReleaseSemantics); |
| |
| if (CObjectType::OwnershipTracked == osOwnershipSemantics && |
| 0 < GetOwnershipCount()) |
| { |
| // We are rentering an object with ownership: we need to skip |
| // the object unsignaling |
| fReenteringObjWithOwnership = true; |
| } |
| |
| if (!fReenteringObjWithOwnership && |
| CObjectType::ThreadReleaseAltersSignalCount == trsThreadReleaseSemantics) |
| { |
| _ASSERT_MSG(0 < GetSignalCount(), |
| "Internal error: operation would make signal count " |
| "negative - object should be signaled at this time " |
| "[signal count=%d]", GetSignalCount()); |
| _ASSERT_MSG(CObjectType::OwnershipTracked != osOwnershipSemantics || |
| 1 == GetSignalCount(), |
| "Ownable objects cannot have signal count greater " |
| "than zero [current SignalCount=%d]\n", |
| GetSignalCount()); |
| |
| // Unsignal the object |
| DecrementSignalCount(); |
| } |
| |
| if (CObjectType::OwnershipTracked == osOwnershipSemantics) |
| { |
| _ASSERT_MSG(0 == GetOwnershipCount() || 0 == GetSignalCount(), |
| "OwnershipCount and SignalCount with conflicting " |
| "values\n"); |
| |
| // Take ownership or increment ownership count. |
| // We do this after the object unsignaling to minimize possibilities |
| // of having both SignalCount and OwnershipCount greater than zero |
| // (see comment in AssignOwnershipToThread) |
| palErr = AssignOwnershipToThread(pthrCurrent, pthrTarget); |
| |
| if (NO_ERROR != palErr) |
| { |
| ERROR("AssignOwnershipToThread failed with error %u; " |
| "ownership data on object with SynchData {shrid=%p p=%p} " |
| "may be corrupted\n", palErr, (void *)m_shridThis, this); |
| } |
| } |
| |
| #ifdef SYNCH_STATISTICS |
| if (NO_ERROR == palErr) |
| { |
| IncrementStatWaitCount(); |
| } |
| #endif |
| return palErr; |
| |
| } |
| |
| /*++ |
| Method: |
| CSynchData::CanWaiterWaitWithoutBlocking |
| |
| Returns whether or not the waiter thread can wait on the target object |
| without blocking (i.e. the objet is signaled) |
| |
| Note: this method must be called while holding the appropriate |
| synchronization locks (the local process synch lock if the target |
| object is local, both local and shared one if the object is shared). |
| --*/ |
| bool CSynchData::CanWaiterWaitWithoutBlocking( |
| CPalThread * pWaiterThread, |
| bool * pfAbandoned) |
| { |
| VALIDATEOBJECT(this); |
| |
| bool fRetVal = (0 < GetSignalCount()); |
| bool fAbandoned = false; |
| bool fOwnershipTracked = (CObjectType::OwnershipTracked == |
| GetObjectType()->GetOwnershipSemantics()); |
| if (fRetVal) |
| { |
| // Object signaled: thread can wait without blocking |
| if (fOwnershipTracked) |
| { |
| fAbandoned = IsAbandoned(); |
| } |
| |
| goto CWWWB_exit; |
| } |
| |
| // Object not signaled: thread can wait without blocking only if the |
| // object is an ownable one, and it is owned by the current thread |
| if (fOwnershipTracked) |
| { |
| _ASSERT_MSG(0 < GetSignalCount() || 0 < GetOwnershipCount(), |
| "Objects with ownership must be either signaled or " |
| "owned by a thread\n"); |
| |
| if ((GetOwnerProcessID() == gPID) && |
| (GetOwnerThread() == pWaiterThread) ) |
| { |
| fRetVal = true; |
| goto CWWWB_exit; |
| } |
| } |
| |
| CWWWB_exit: |
| *pfAbandoned = fAbandoned; |
| return fRetVal; |
| } |
| |
| /*++ |
| Method: |
| CSynchData::Signal |
| |
| Sets the signal count of the object owning the target SynchData, |
| possibly triggering awakening of waiting threads. |
| |
| Note: this method must be called while holding the appropriate |
| synchronization locks (the local process synch lock if the target |
| object is local, both local and shared one if the object is shared). |
| --*/ |
| void CSynchData::Signal( |
| CPalThread * pthrCurrent, |
| LONG lSignalCount, |
| bool fWorkerThread) |
| { |
| VALIDATEOBJECT(this); |
| |
| bool fThreadReleased = false; |
| bool fDelegatedSignaling = false; |
| bool fReleaseAltersSignalCount = |
| (CObjectType::ThreadReleaseAltersSignalCount == |
| GetObjectType()->GetThreadReleaseSemantics()); |
| |
| _ASSERTE(0 <= lSignalCount); |
| |
| // Preset the signal count to the new value, so that it can be used |
| // by ReleaseFirstWaiter when delegating signaling to another process |
| m_lSignalCount = lSignalCount; |
| |
| while (m_lSignalCount > 0) |
| { |
| fThreadReleased = ReleaseFirstWaiter(pthrCurrent, |
| &fDelegatedSignaling, |
| fWorkerThread); |
| if (!fThreadReleased) |
| { |
| // No more threads to release: break out of the loop |
| // keeping the current signal count |
| break; |
| } |
| if (fReleaseAltersSignalCount) |
| { |
| // Adjust signal count |
| m_lSignalCount--; |
| } |
| if (fDelegatedSignaling) |
| { |
| // Object signaling has been delegated |
| m_lSignalCount = 0; |
| } |
| } |
| |
| _ASSERT_MSG(CObjectType::OwnershipTracked != |
| GetObjectType()->GetOwnershipSemantics() || |
| 0 == GetOwnershipCount() || 0 == GetSignalCount(), |
| "Conflicting values for SignalCount [%d] and " |
| "OwnershipCount [%d]\n", |
| GetOwnershipCount(), GetSignalCount()); |
| |
| _ASSERT_MSG(otiMutex != m_otiObjectTypeId || m_lSignalCount <= 1, |
| "Mutex with invalid singal count\n"); |
| |
| return; |
| } |
| |
| /*++ |
| Method: |
| CSynchData::ReleaseFirstWaiter |
| |
| Releases the first thread from the front of the list of waiting threads |
| whose wait is fully satisfied, possibly triggering remote awakening (if |
| the target thread lives in a different process) or object signaling |
| delegation (if the target thread lives in a different processing and it |
| is blocked on a wait-all). |
| |
| Note: this method must be called while holding the appropriate |
| synchronization locks (the local process synch lock if the target |
| object is local, both local and shared one if the object is shared). |
| --*/ |
| bool CSynchData::ReleaseFirstWaiter( |
| CPalThread * pthrCurrent, |
| bool * pfDelegated, |
| bool fWorkerThread) |
| { |
| PAL_ERROR palErr = NO_ERROR; |
| bool fSharedSynchLock = false; |
| bool fSharedObject = (SharedObject == GetObjectDomain()); |
| bool fThreadAwakened = false; |
| bool fDelegatedSignaling = false; |
| DWORD * pdwWaitState; |
| DWORD dwObjIdx; |
| SharedID shridItem = NULLSharedID, shridNextItem = NULLSharedID; |
| WaitingThreadsListNode * pwtlnItem, * pwtlnNextItem; |
| DWORD dwPid = gPID; |
| CPalSynchronizationManager * pSynchManager = |
| CPalSynchronizationManager::GetInstance(); |
| |
| VALIDATEOBJECT(this); |
| |
| *pfDelegated = false; |
| |
| if (fSharedObject) |
| { |
| shridItem = GetWTLHeadShmPtr(); |
| pwtlnItem = SharedIDToTypePointer(WaitingThreadsListNode, shridItem); |
| } |
| else |
| { |
| pwtlnItem = GetWTLHeadPtr(); |
| } |
| |
| while (pwtlnItem) |
| { |
| VALIDATEOBJECT(pwtlnItem); |
| |
| WaitCompletionState wcsWaitCompletionState; |
| bool fWaitAll = (0 != (WTLN_FLAG_WAIT_ALL & pwtlnItem->dwFlags)); |
| pdwWaitState = SharedIDToTypePointer(DWORD, |
| pwtlnItem->shridWaitingState); |
| |
| if (fSharedObject) |
| { |
| shridNextItem = pwtlnItem->ptrNext.shrid; |
| pwtlnNextItem = SharedIDToTypePointer(WaitingThreadsListNode, |
| shridNextItem); |
| } |
| else |
| { |
| pwtlnNextItem = pwtlnItem->ptrNext.ptr; |
| } |
| |
| if (fWaitAll) |
| { |
| // Wait All: we need to find out whether the wait is satisfied, |
| // or it is not, or if that cannot be determined from within |
| // this process (WaitMayBeSatisfied); in this case we need to |
| // delegate the object signaling to the process hosting the |
| // thread that owns the current target WaitingThreadsListNode |
| |
| // If the target object is local (fSharedObject == false) |
| // we're probably not holding the shared lock. |
| // If the wait is not a LocalWait, it involves at least one |
| // shared object. If that is the case, at this time we need |
| // to grab the shared lock. In fact IsRestOfWaitAllSatisfied |
| // and UnsignalRestOfLocalAwakeningWaitAll must be called |
| // atomically to prevent that another thread living |
| // in a different process could race with us stealing the |
| // signaling from one of the objects involved in the wait-all. |
| // |
| // Note: pwtlnItem->ptwiWaitInfo is valid only if the target |
| // wait originates in the current process. Anyway in the |
| // following 'if' we don't need to check that since we are |
| // already making sure that the object is local (!fSharedObject). |
| // If a wait involves at least one object local to this process, |
| // it can only be a wait performed by a thread in the current |
| // process, therefore pwtlnItem->ptwiWaitInfo is valid. |
| |
| _ASSERTE(fSharedObject || pwtlnItem->dwProcessId == gPID); |
| |
| if (!fSharedSynchLock && !fSharedObject && |
| LocalWait != pwtlnItem->ptwiWaitInfo->wdWaitDomain) |
| { |
| CPalSynchronizationManager::AcquireSharedSynchLock(pthrCurrent); |
| fSharedSynchLock = true; |
| } |
| |
| // First check if the current target node is already marked for |
| // wait all check in progress, and in case skip it by setting |
| // wcsWaitCompletionState to WaitIsNotSatisfied |
| bool fMarkedForDelegatedObjectSingalingInProgress = |
| (0 != (WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS & pwtlnItem->dwFlags)); |
| |
| wcsWaitCompletionState = |
| fMarkedForDelegatedObjectSingalingInProgress ? WaitIsNotSatisfied : |
| IsRestOfWaitAllSatisfied(pwtlnItem); |
| } |
| else |
| { |
| // Normal Wait: the wait is satisfied by definition |
| wcsWaitCompletionState = WaitIsSatisfied; |
| } |
| |
| if (WaitIsSatisfied == wcsWaitCompletionState) |
| { |
| // |
| // Target wait is satisfied |
| // |
| TRACE("Trying to switch wait state [%p] from WAIT/ALERTABLE " |
| "to ACTIVE for thread=%u\n", |
| pdwWaitState, pwtlnItem->dwThreadId); |
| |
| if (CPalSynchronizationManager::InterlockedAwaken(pdwWaitState, FALSE)) |
| { |
| TRACE("Succeeded switching wait state [%p] from WAIT/ALERTABLE " |
| "to TWS_ACTIVE for trhead=%u\n", |
| pdwWaitState, pwtlnItem->dwThreadId); |
| |
| dwObjIdx = pwtlnItem->dwObjIndex; |
| |
| if (dwPid == pwtlnItem->dwProcessId) |
| { |
| /////////////////////////// |
| // |
| // Local Thread Awakening |
| // |
| /////////////////////////// |
| ThreadWaitInfo * ptwiWaitInfo = pwtlnItem->ptwiWaitInfo; |
| bool fAbandoned = false; |
| |
| if (CObjectType::OwnershipTracked == |
| GetObjectType()->GetOwnershipSemantics()) |
| { |
| // Get the abandoned status before resetting it by |
| // assigning ownership to target thread |
| fAbandoned = IsAbandoned(); |
| |
| // Assign ownership to target thread |
| // Note: This will cause both ownership count and |
| // signal count to be greater than zero at the |
| // same time; the signal count will be anyway |
| // decremented immediately by the caller |
| // CsynchData::Signal |
| palErr = AssignOwnershipToThread(pthrCurrent, |
| ptwiWaitInfo->pthrOwner); |
| if (NO_ERROR != palErr) |
| { |
| ERROR("Synch Worker: AssignOwnershipToThread " |
| "failed with error %u; ownership data on " |
| "object with SynchData %p may be " |
| "corrupted\n", palErr, this); |
| } |
| } |
| |
| if (fWaitAll) |
| { |
| // Wait all satisfied: unsignal other objects |
| // involved in the wait |
| CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll( |
| pthrCurrent, |
| ptwiWaitInfo->pthrOwner, |
| pwtlnItem, |
| this); |
| } |
| |
| TRACE("Unregistering wait for thread %u and waking it up " |
| "[pdwWaitState=%p]\n", pwtlnItem->dwThreadId, |
| pdwWaitState); |
| |
| // Unregister the wait |
| pSynchManager->UnRegisterWait(pthrCurrent, |
| ptwiWaitInfo, |
| fSharedObject || fSharedSynchLock); |
| |
| // After UnRegisterWait pwtlnItem is invalid |
| pwtlnItem = NULL; |
| |
| palErr = CPalSynchronizationManager::WakeUpLocalThread( |
| pthrCurrent, |
| ptwiWaitInfo->pthrOwner, |
| fAbandoned ? MutexAbondoned : WaitSucceeded, |
| dwObjIdx); |
| |
| if (NO_ERROR != palErr) |
| { |
| ERROR("Failed to wakeup local thread %#x: " |
| "object signaling may be " |
| "lost\n", ptwiWaitInfo->pthrOwner->GetThreadId()); |
| } |
| } |
| else |
| { |
| /////////////////////////// |
| // |
| // Remote Thread Awakening |
| // |
| /////////////////////////// |
| |
| // Note: if we are here, this cannot be a wait-all |
| _ASSERT_MSG(!fWaitAll, |
| "Control should never reach this point if " |
| "target wait is a wait-all\n"); |
| |
| // Wake up remote thread |
| palErr = CPalSynchronizationManager::WakeUpRemoteThread(shridItem); |
| |
| if (NO_ERROR != palErr) |
| { |
| ERROR("Failed to dispatch remote awakening cmd to " |
| "worker thread in process pid=%d to wake up" |
| "thread tid=%#x; object signaling may be " |
| "lost\n", pwtlnItem->dwProcessId, |
| pwtlnItem->dwThreadId); |
| } |
| } |
| |
| // A thread has been awakened |
| fThreadAwakened = true; |
| |
| // break out of the while loop |
| break; |
| } |
| } |
| else if (WaitMayBeSatisfied == wcsWaitCompletionState) |
| { |
| ////////////////////////////////////////// |
| // |
| // Wait All with remote thread awakening |
| // |
| ////////////////////////////////////////// |
| |
| // |
| // We need to transfer the object signaling to the process |
| // hosting the target waiter thread |
| // |
| |
| _ASSERT_MSG(fWaitAll, |
| "IsRestOfWaitAllSatisfied() apparently " |
| "returned -1 on a normal (non wait all) " |
| "wait\n"); |
| _ASSERT_MSG(fSharedObject, |
| "About to delegate object signaling to a remote " |
| "process, but the signaled object is actually " |
| "local\n"); |
| |
| // Delegate object signaling to target process |
| palErr = CPalSynchronizationManager::DelegateSignalingToRemoteProcess( |
| pthrCurrent, |
| pwtlnItem->dwProcessId, |
| pwtlnItem->ptrOwnerObjSynchData.shrid); |
| |
| TRACE("Delegating object signaling for SynchData shrid=%p\n", |
| (VOID *)pwtlnItem->ptrOwnerObjSynchData.shrid); |
| |
| if (NO_ERROR == palErr) |
| { |
| // A remote thread will be awakened |
| // This will also cause the object to be unsignaled by the |
| // code calling ReleaseFirstWaiter before releasing the |
| // synch locks, so no other WaitForMultipleObjects |
| // involving the target object may race stealing this |
| // particuklar object signaling |
| fThreadAwakened = true; |
| |
| fDelegatedSignaling = true; |
| |
| // break out of the while loop |
| break; |
| } |
| else |
| { |
| ERROR("Failed to delegate object signaling to remote " |
| "process %d. Looking for another waiter.\n", |
| pwtlnItem->dwProcessId); |
| |
| // Go on: a different target waiter will be selected |
| } |
| } |
| |
| if (fWorkerThread && fWaitAll && (dwPid == pwtlnItem->dwProcessId)) |
| { |
| // Mark the target wait for object signaling |
| CPalSynchronizationManager::MarkWaitForDelegatedObjectSignalingInProgress( |
| pthrCurrent, |
| pwtlnItem); |
| } |
| |
| // Go to the next item |
| shridItem = shridNextItem; |
| pwtlnItem = pwtlnNextItem; |
| } |
| |
| if (fDelegatedSignaling) |
| { |
| *pfDelegated = true; |
| } |
| else if (fWorkerThread) |
| { |
| // Reset 'delegated object signaling in progress' flags |
| CPalSynchronizationManager::UnmarkTWListForDelegatedObjectSignalingInProgress( |
| this); |
| } |
| |
| if (fSharedSynchLock) |
| { |
| CPalSynchronizationManager::ReleaseSharedSynchLock(pthrCurrent); |
| } |
| return fThreadAwakened; |
| } |
| |
| /*++ |
| Method: |
| CSynchData::Signal |
| |
| Releases all the threads waiting on this object and living in the current |
| process. |
| |
| Note: this method must be called while holding the appropriate |
| synchronization locks (the local process synch lock if the target |
| object is local, both local and shared one if the object is shared). |
| --*/ |
| LONG CSynchData::ReleaseAllLocalWaiters( |
| CPalThread * pthrCurrent) |
| { |
| PAL_ERROR palErr = NO_ERROR; |
| LONG lAwakenedCount = 0; |
| bool fSharedSynchLock = false; |
| bool fSharedObject = (SharedObject == GetObjectDomain()); |
| DWORD * pdwWaitState; |
| DWORD dwObjIdx; |
| SharedID shridItem = NULLSharedID, shridNextItem = NULLSharedID; |
| WaitingThreadsListNode * pwtlnItem, * pwtlnNextItem; |
| DWORD dwPid = gPID; |
| CPalSynchronizationManager * pSynchManager = |
| CPalSynchronizationManager::GetInstance(); |
| |
| VALIDATEOBJECT(this); |
| |
| if (fSharedObject) |
| { |
| shridItem = GetWTLHeadShmPtr(); |
| pwtlnItem = SharedIDToTypePointer(WaitingThreadsListNode, shridItem); |
| } |
| else |
| { |
| pwtlnItem = GetWTLHeadPtr(); |
| } |
| |
| while (pwtlnItem) |
| { |
| VALIDATEOBJECT(pwtlnItem); |
| |
| bool fWaitAll = (0 != (WTLN_FLAG_WAIT_ALL & pwtlnItem->dwFlags)); |
| pdwWaitState = SharedIDToTypePointer(DWORD, |
| pwtlnItem->shridWaitingState); |
| |
| if (fSharedObject) |
| { |
| shridNextItem = pwtlnItem->ptrNext.shrid; |
| pwtlnNextItem = SharedIDToTypePointer(WaitingThreadsListNode, |
| shridNextItem); |
| } |
| else |
| { |
| pwtlnNextItem = pwtlnItem->ptrNext.ptr; |
| } |
| |
| // See note in similar spot in ReleaseFirstWaiter |
| |
| _ASSERTE(fSharedObject || pwtlnItem->dwProcessId == gPID); |
| |
| if (!fSharedSynchLock && !fSharedObject && |
| LocalWait != pwtlnItem->ptwiWaitInfo->wdWaitDomain) |
| { |
| CPalSynchronizationManager::AcquireSharedSynchLock(pthrCurrent); |
| fSharedSynchLock = true; |
| } |
| |
| if( dwPid == pwtlnItem->dwProcessId && |
| (!fWaitAll || WaitIsSatisfied == IsRestOfWaitAllSatisfied(pwtlnItem)) ) |
| { |
| // |
| // Target wait is satisfied |
| // |
| TRACE("Trying to switch wait state [%p] from WAIT/ALERTABLE " |
| "to ACTIVE for thread=%u\n", |
| pdwWaitState, pwtlnItem->dwThreadId); |
| |
| if (CPalSynchronizationManager::InterlockedAwaken(pdwWaitState, FALSE)) |
| { |
| TRACE("Succeeded switching wait state [%p] from WAIT/ALERTABLE " |
| "to TWS_ACTIVE for trhead=%u\n", |
| pdwWaitState, pwtlnItem->dwThreadId); |
| |
| dwObjIdx = pwtlnItem->dwObjIndex; |
| |
| ThreadWaitInfo * ptwiWaitInfo = pwtlnItem->ptwiWaitInfo; |
| bool fAbandoned = false; |
| |
| if (CObjectType::OwnershipTracked == |
| GetObjectType()->GetOwnershipSemantics()) |
| { |
| // Get the abandoned status before resetting it by |
| // assigning ownership to target thread |
| fAbandoned = IsAbandoned(); |
| |
| // Assign ownership to target thread |
| palErr = AssignOwnershipToThread(pthrCurrent, |
| ptwiWaitInfo->pthrOwner); |
| if (NO_ERROR != palErr) |
| { |
| ERROR("Synch Worker: AssignOwnershipToThread " |
| "failed with error %u; ownership data on " |
| "object with SynchData %p may be " |
| "corrupted\n", palErr, this); |
| } |
| } |
| |
| if (fWaitAll) |
| { |
| // Wait all satisfied: unsignal other objects |
| // involved in the wait |
| CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll( |
| pthrCurrent, |
| ptwiWaitInfo->pthrOwner, |
| pwtlnItem, |
| this); |
| } |
| |
| TRACE("Unregistering wait for thread %u and waking it up " |
| "[pdwWaitState=%p]\n", pwtlnItem->dwThreadId, |
| pdwWaitState); |
| |
| // Unregister the wait |
| pSynchManager->UnRegisterWait(pthrCurrent, |
| ptwiWaitInfo, |
| fSharedObject || fSharedSynchLock); |
| |
| // After UnRegisterWait pwtlnItem is invalid |
| pwtlnItem = NULL; |
| |
| palErr = CPalSynchronizationManager::WakeUpLocalThread( |
| pthrCurrent, |
| ptwiWaitInfo->pthrOwner, |
| fAbandoned ? MutexAbondoned : WaitSucceeded, |
| dwObjIdx); |
| |
| if (NO_ERROR != palErr) |
| { |
| ERROR("Failed to wakeup local thread %#x: " |
| "object signaling may be " |
| "lost\n", ptwiWaitInfo->pthrOwner->GetThreadId()); |
| } |
| else |
| { |
| // A thread has been awakened |
| lAwakenedCount++; |
| } |
| } |
| } |
| |
| // Go to the next item |
| shridItem = shridNextItem; |
| pwtlnItem = pwtlnNextItem; |
| } |
| |
| if (fSharedSynchLock) |
| { |
| CPalSynchronizationManager::ReleaseSharedSynchLock(pthrCurrent); |
| } |
| return lAwakenedCount; |
| } |
| |
| /*++ |
| Method: |
| CSynchData::IsRestOfWaitAllSatisfied |
| |
| Returns whether or not the current wait-all operation is fully satisfied, |
| assuming the current target object as signaled (i.e. whether or not all the |
| involved object, except the current one, are signaled). |
| It returns: |
| - WaitIsNotSatisfied if the wait-all is not fully satisfied. |
| - WaitIsSatisfied if the wait-all is fully satisfied. |
| - WaitMayBeSatisfied if the target thread lives in a different process and |
| therefore the wait may involve objects local to the remote process, and |
| as result is generally not possible to say whther or not the wait-all is |
| fully satisfied from the current process. |
| |
| Note: this method must be called while holding the synchronization locks |
| appropriate to all the objects involved in the wait-all. If any |
| of the objects is shared, the caller must own both local and |
| shared synch locks; if no shared object is involved in the wait, |
| only the local synch lock is needed. |
| --*/ |
| WaitCompletionState CSynchData::IsRestOfWaitAllSatisfied( |
| WaitingThreadsListNode * pwtlnNode) |
| { |
| int iSignaledOrOwnedObjCount = 0; |
| int iTgtCount = 0; |
| int i; |
| WaitCompletionState wcsWaitCompletionState = WaitIsNotSatisfied; |
| CSynchData * psdSynchDataItem = NULL; |
| ThreadWaitInfo * ptwiWaitInfo = NULL; |
| |
| VALIDATEOBJECT(this); |
| VALIDATEOBJECT(pwtlnNode); |
| |
| _ASSERT_MSG(0 != (WTLN_FLAG_WAIT_ALL & pwtlnNode->dwFlags), |
| "IsRestOfWaitAllSatisfied() called on a normal " |
| "(non wait all) wait"); |
| _ASSERT_MSG((SharedObject == GetObjectDomain()) == |
| (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnNode->dwFlags)), |
| "WTLN_FLAG_OWNER_OBJECT_IS_SHARED in WaitingThreadsListNode " |
| "not consistent with target object's domain\n"); |
| |
| if(gPID != pwtlnNode->dwProcessId) |
| { |
| //////////////////////////// |
| // |
| // Remote Thread Awakening |
| // |
| //////////////////////////// |
| |
| // Cannot determine whether or not the wait all is satisfied from |
| // this process |
| wcsWaitCompletionState = WaitMayBeSatisfied; |
| goto IROWAS_exit; |
| } |
| |
| /////////////////////////// |
| // |
| // Local Thread Awakening |
| // |
| /////////////////////////// |
| |
| ptwiWaitInfo = pwtlnNode->ptwiWaitInfo; |
| |
| iTgtCount = ptwiWaitInfo->lObjCount; |
| for (i=0; i < iTgtCount; i++) |
| { |
| WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i]; |
| bool fRetVal; |
| bool fIsAbandoned; |
| |
| VALIDATEOBJECT(pwtlnItem); |
| |
| if (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnItem->dwFlags)) |
| { |
| psdSynchDataItem = SharedIDToTypePointer(CSynchData, |
| pwtlnItem->ptrOwnerObjSynchData.shrid); |
| } |
| else |
| { |
| psdSynchDataItem = pwtlnItem->ptrOwnerObjSynchData.ptr; |
| } |
| |
| VALIDATEOBJECT(psdSynchDataItem); |
| |
| if (pwtlnItem == pwtlnNode) |
| { |
| _ASSERT_MSG (this == psdSynchDataItem, |
| "pwtlnNode and pwtlnItem match, but this " |
| "and psdSynchDataItem don't\n"); |
| |
| // The target object (the one related to pwtlnNode) is counted as |
| // signaled/owned without checking it (also if it is not, as |
| // it normally happens when this method is called) |
| iSignaledOrOwnedObjCount++; |
| continue; |
| } |
| |
| fRetVal = psdSynchDataItem->CanWaiterWaitWithoutBlocking( |
| ptwiWaitInfo->pthrOwner, |
| &fIsAbandoned); |
| |
| if (fRetVal) |
| { |
| iSignaledOrOwnedObjCount++; |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if (iSignaledOrOwnedObjCount < iTgtCount) |
| { |
| wcsWaitCompletionState = WaitIsNotSatisfied; |
| } |
| else |
| { |
| wcsWaitCompletionState = WaitIsSatisfied; |
| } |
| |
| IROWAS_exit: |
| TRACE("IsRestOfWaitAllSatisfied() returning %u \n", wcsWaitCompletionState); |
| |
| return wcsWaitCompletionState; |
| } |
| |
| |
| /*++ |
| Method: |
| CSynchData::SetOwner |
| |
| Blindly sets the thread whose CPalThread is passed as argument, as the |
| owner of the current object. |
| WARNING: this method discards any previous ownership data and does not |
| update the list of the object owned by the owner thread. |
| |
| Note: this method must be called while holding the appropriate |
| synchronization locks (the local process synch lock if the target |
| object is local, both local and shared one if the object is shared). |
| --*/ |
| void CSynchData::SetOwner(CPalThread * pOwnerThread) |
| { |
| VALIDATEOBJECT(this); |
| |
| m_dwOwnerPid = gPID; |
| m_dwOwnerTid = pOwnerThread->GetThreadId(); |
| m_pOwnerThread = pOwnerThread; |
| } |
| |
| /*++ |
| Method: |
| CSynchData::ResetOwnership |
| |
| Resets current object's ownership data |
| |
| Note: this method must be called while holding the appropriate |
| synchronization locks (the local process synch lock if the target |
| object is local, both local and shared one if the object is shared). |
| --*/ |
| void CSynchData::ResetOwnership() |
| { |
| VALIDATEOBJECT(this); |
| |
| m_lOwnershipCount = 0; |
| m_dwOwnerPid = 0; |
| m_dwOwnerTid = 0; |
| m_pOwnerThread = NULL; |
| m_poolnOwnedObjectListNode = NULL; |
| } |
| |
| /*++ |
| Method: |
| CSynchData::AssignOwnershipToThread |
| |
| Assigns thw ownership of the current object to the target thread, performing |
| all the operations neede to mantain the correct status of ownership data, |
| also handling recursive object ownership acquisition |
| |
| Note: this method must be called while holding the appropriate |
| synchronization locks (the local process synch lock if the target |
| object is local, both local and shared one if the object is shared). |
| --*/ |
| PAL_ERROR CSynchData::AssignOwnershipToThread( |
| CPalThread * pthrCurrent, |
| CPalThread * pthrTarget) |
| { |
| // Note: when this method is called by ReleaseFirstWaiter there is |
| // a small time window in which both SignalCount and |
| // OwnershipCount can be greater than zero (which normally |
| // is illegal). Anyway that is fine since ReleaseFirstWaiter |
| // will restore the value right after, and such situation |
| // takes place while holding synchroniztion locks, so no |
| // other thread/process can access the object. |
| |
| PAL_ERROR palErr = NO_ERROR; |
| |
| _ASSERT_MSG(CObjectType::OwnershipTracked == |
| GetObjectType()->GetOwnershipSemantics(), |
| "AssignOwnershipToThread called on a non-ownable " |
| "CSynchData [this=%p OwnershipSemantics=%u]\n", this, |
| GetObjectType()->GetOwnershipSemantics()); |
| |
| |
| if (0 < m_lOwnershipCount) |
| { |
| // |
| // Object already owned, incrementing ownership count |
| // |
| _ASSERT_MSG(0 == GetSignalCount(), |
| "Conflicting OwnershipCount and SignalCount values\n"); |
| |
| _ASSERT_MSG(pthrTarget == m_pOwnerThread && gPID == m_dwOwnerPid, |
| "Attempting to assign ownership of CSynchData %p to " |
| "thread {pid=%#x tid=%#x} while it is currently owned " |
| "by thread {pid=%#x tid=%#x}\n", this, |
| gPID, pthrTarget->GetThreadId(), |
| m_dwOwnerPid, m_pOwnerThread->GetThreadId()); |
| |
| m_lOwnershipCount++; |
| |
| TRACE("Incrementing ownership count for object with " |
| "SynchData %p owned by thread %#x [new count=%d]\n", |
| this, pthrTarget->GetThreadId(), m_lOwnershipCount); |
| } |
| else |
| { |
| // |
| // Acquiring currently not owned object |
| // |
| CPalSynchronizationManager * pSynchManager = |
| CPalSynchronizationManager::GetInstance(); |
| OwnedObjectsListNode * pooln; |
| |
| pooln = pSynchManager->CacheGetOwnedObjsListNode(pthrCurrent); |
| if (NULL == pooln) |
| { |
| ERROR("Out of memory while acquiring mutex ownership"); |
| // In this case we bail out. It will result in no |
| // thread being awakend, which may cause deadlock, |
| // but it is anyway better than corrupting the |
| // ownership list |
| palErr = ERROR_NOT_ENOUGH_MEMORY; |
| goto AOTT_exit; |
| } |
| |
| TRACE("Assigning ownable object with SynchData %p to " |
| "thread %#x\n", |
| this, pthrTarget->GetThreadId()); |
| |
| // Set ownership data |
| SetOwner(pthrTarget); |
| SetOwnershipListNode(pooln); |
| SetOwnershipCount(1); |
| SetAbandoned(false); |
| |
| // Add object to list of owned objs for current thread |
| pooln->pPalObjSynchData = this; |
| AddRef(); |
| pthrTarget->synchronizationInfo.AddObjectToOwnedList(pooln); |
| } |
| |
| AOTT_exit: |
| return palErr; |
| } |
| |
| /*++ |
| Method: |
| CSynchData::WaiterEnqueue |
| |
| Adds the WaitingThreadsListNode passed as argument at the end of the |
| list of WaitingThreadsListNode for the current object, representing |
| the threads waiting on the current object. The target SynchData is |
| assumed to be local to the current process |
| |
| Note: this method must be called while holding the local process |
| synchronization lock. |
| --*/ |
| void CSynchData::WaiterEnqueue(WaitingThreadsListNode * pwtlnNewNode) |
| { |
| VALIDATEOBJECT(this); |
| VALIDATEOBJECT(pwtlnNewNode); |
| |
| _ASSERT_MSG(ProcessLocalObject == GetObjectDomain(), |
| "Trying to enqueue a WaitingThreadsListNode as local " |
| "on a shared object\n"); |
| _ASSERT_MSG(0 == (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnNewNode->dwFlags), |
| "Trying to add a WaitingThreadsListNode marked as shared " |
| "as it was a local one\n"); |
| |
| WaitingThreadsListNode * pwtlnCurrLast = m_ptrWTLTail.ptr; |
| |
| pwtlnNewNode->ptrNext.ptr = NULL; |
| if (NULL == pwtlnCurrLast) |
| { |
| _ASSERT_MSG(NULL == m_ptrWTLHead.ptr, |
| "Corrupted waiting list on local CSynchData @ %p\n", |
| this); |
| |
| pwtlnNewNode->ptrPrev.ptr = NULL; |
| m_ptrWTLHead.ptr = pwtlnNewNode; |
| m_ptrWTLTail.ptr = pwtlnNewNode; |
| } |
| else |
| { |
| VALIDATEOBJECT(pwtlnCurrLast); |
| |
| pwtlnNewNode->ptrPrev.ptr = pwtlnCurrLast; |
| pwtlnCurrLast->ptrNext.ptr = pwtlnNewNode; |
| m_ptrWTLTail.ptr = pwtlnNewNode; |
| } |
| |
| m_ulcWaitingThreads += 1; |
| |
| return; |
| } |
| |
| /*++ |
| Method: |
| CSynchData::SharedWaiterEnqueue |
| |
| Adds the WaitingThreadsListNode passed as argument at the end of the |
| list of WaitingThreadsListNode for the current object, representing |
| the threads waiting on the current object. The target SynchData is |
| assumed to be shared among processes |
| |
| Note: this method must be called while holding both local and shared |
| synchronization locks. |
| --*/ |
| void CSynchData::SharedWaiterEnqueue(SharedID shridNewNode) |
| { |
| VALIDATEOBJECT(this); |
| |
| _ASSERT_MSG(SharedObject == GetObjectDomain(), |
| "Trying to enqueue a WaitingThreadsListNode as shared " |
| "on a local object\n"); |
| |
| SharedID shridCurrLast; |
| WaitingThreadsListNode * pwtlnCurrLast, * pwtlnNewNode; |
| |
| shridCurrLast = m_ptrWTLTail.shrid; |
| pwtlnCurrLast = SharedIDToTypePointer(WaitingThreadsListNode, shridCurrLast); |
| pwtlnNewNode = SharedIDToTypePointer(WaitingThreadsListNode, shridNewNode); |
| |
| _ASSERT_MSG(1 == (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnNewNode->dwFlags), |
| "Trying to add a WaitingThreadsListNode marked as local " |
| "as it was a shared one\n"); |
| |
| VALIDATEOBJECT(pwtlnNewNode); |
| |
| pwtlnNewNode->ptrNext.shrid = NULLSharedID; |
| if (NULL == pwtlnCurrLast) |
| { |
| _ASSERT_MSG(NULLSharedID == m_ptrWTLHead.shrid, |
| "Corrupted waiting list on shared CSynchData at " |
| "{shrid=%p, p=%p}\n", m_shridThis, this); |
| |
| pwtlnNewNode->ptrPrev.shrid = NULLSharedID; |
| m_ptrWTLHead.shrid = shridNewNode; |
| m_ptrWTLTail.shrid = shridNewNode; |
| } |
| else |
| { |
| VALIDATEOBJECT(pwtlnCurrLast); |
| |
| pwtlnNewNode->ptrPrev.shrid = shridCurrLast; |
| pwtlnCurrLast->ptrNext.shrid = shridNewNode; |
| m_ptrWTLTail.shrid = shridNewNode; |
| } |
| |
| m_ulcWaitingThreads += 1; |
| |
| return; |
| } |
| |
| #ifdef SYNCH_OBJECT_VALIDATION |
| CSynchData::~CSynchData() |
| { |
| ValidateObject(true); |
| InvalidateObject(); |
| } |
| /*++ |
| Method: |
| CSynchData::ValidateObject |
| |
| Makes sure that the signature at the beginning and at the end of the |
| current object are those of a currently alive object (i.e. the object |
| has been constructed and does not appear to have been overwritten) |
| --*/ |
| void CSynchData::ValidateObject(bool fDestructor) |
| { |
| TRACE("Verifying in-use CSynchData @ %p\n", this); |
| _ASSERT_MSG(HeadSignature == m_dwDebugHeadSignature, |
| "CSynchData header signature corruption [p=%p]", this); |
| _ASSERT_MSG(TailSignature == m_dwDebugTailSignature, |
| "CSynchData trailer signature corruption [p=%p]", this); |
| _ASSERT_MSG((fDestructor && 0 == m_lRefCount) || |
| (!fDestructor && 0 < m_lRefCount), |
| "CSynchData %p with NULL reference count\n", this); |
| } |
| /*++ |
| Method: |
| CSynchData::ValidateEmptyObject |
| |
| Makes sure that the signature at the beginning and at the end of the |
| current object are not those of a currently alive object (i.e. the |
| object has not yet been constructed or it has alread been destructed) |
| --*/ |
| void CSynchData::ValidateEmptyObject() |
| { |
| TRACE("Verifying empty CSynchData @ %p\n", this); |
| _ASSERT_MSG(HeadSignature != m_dwDebugHeadSignature, |
| "CSynchData header previously signed [p=%p]", this); |
| _ASSERT_MSG(TailSignature != m_dwDebugTailSignature, |
| "CSynchData trailer previously signed [p=%p]", this); |
| } |
| /*++ |
| Method: |
| CSynchData::InvalidateObject |
| |
| Turns signatures from alive object to destructed object |
| --*/ |
| void CSynchData::InvalidateObject() |
| { |
| TRACE("Invalidating CSynchData @ %p\n", this); |
| m_dwDebugHeadSignature = EmptySignature; |
| m_dwDebugTailSignature = EmptySignature; |
| } |
| #endif // SYNCH_OBJECT_VALIDATION |
| } |
| |