1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 //*****************************************************************************
9 //*****************************************************************************
12 /* ------------------------------------------------------------------------- *
14 * ------------------------------------------------------------------------- */
16 CordbHashTable::~CordbHashTable()
20 for (CordbHashEntry *entry = (CordbHashEntry *) FindFirstEntry(&find);
22 entry = (CordbHashEntry *) FindNextEntry(&find))
23 entry->pBase->InternalRelease();
26 HRESULT CordbHashTable::UnsafeAddBase(CordbBase *pBase)
35 HRESULT res = NewInit(m_iBuckets, sizeof(CordbHashEntry), 0xffff);
45 CordbHashEntry *entry = (CordbHashEntry *) Add(HASH((ULONG_PTR)pBase->m_id));
55 pBase->InternalAddRef();
59 bool CordbHashTable::IsInitialized()
64 CordbBase *CordbHashTable::UnsafeGetBase(ULONG_PTR id, BOOL fFab)
68 CordbHashEntry *entry = NULL;
73 entry = (CordbHashEntry *) Find(HASH((ULONG_PTR)id), KEY(id));
74 return (entry ? entry->pBase : NULL);
77 HRESULT CordbHashTable::UnsafeSwapBase(CordbBase *pOldBase, CordbBase *pNewBase)
85 ULONG_PTR id = (ULONG_PTR)pOldBase->m_id;
88 = (CordbHashEntry *) Find(HASH((ULONG_PTR)id), KEY(id));
95 _ASSERTE(entry->pBase == pOldBase);
96 entry->pBase = pNewBase;
98 // release the hash table's reference to the old base and transfer it
100 pOldBase->InternalRelease();
101 pNewBase->InternalAddRef();
107 CordbBase *CordbHashTable::UnsafeRemoveBase(ULONG_PTR id)
117 CordbHashEntry *entry
118 = (CordbHashEntry *) Find(HASH((ULONG_PTR)id), KEY(id));
125 CordbBase *base = entry->pBase;
127 Delete(HASH((ULONG_PTR)id), (HASHENTRY *) entry);
129 base->InternalRelease();
134 CordbBase *CordbHashTable::UnsafeFindFirst(HASHFIND *find)
137 return UnsafeUnlockedFindFirst(find);
140 CordbBase *CordbHashTable::UnsafeUnlockedFindFirst(HASHFIND *find)
142 CordbHashEntry *entry = (CordbHashEntry *) FindFirstEntry(find);
150 CordbBase *CordbHashTable::UnsafeFindNext(HASHFIND *find)
153 return UnsafeUnlockedFindNext(find);
156 CordbBase *CordbHashTable::UnsafeUnlockedFindNext(HASHFIND *find)
158 CordbHashEntry *entry = (CordbHashEntry *) FindNextEntry(find);
166 /* ------------------------------------------------------------------------- *
167 * Hash Table Enumerator class
168 * ------------------------------------------------------------------------- */
170 // This constructor is part of 2 phase construction.
171 // Use the BuildOrThrow method to instantiate.
172 CordbHashTableEnum::CordbHashTableEnum(
173 CordbBase * pOwnerObj, NeuterList * pOwnerList,
174 CordbHashTable *table,
177 : CordbBase(pOwnerObj->GetProcess(), 0, enumCordbHashTableEnum),
178 m_pOwnerObj(pOwnerObj),
179 m_pOwnerNeuterList(pOwnerList),
190 //---------------------------------------------------------------------------------------
192 // Build a new Hash enumerator or throw
196 // pOwnerList - neuter list to add to
197 // table - hash table to enumerate.
198 // id - guid of objects to enumerate
199 // pHolder - holder to get ownership.
201 void CordbHashTableEnum::BuildOrThrow(
202 CordbBase * pOwnerObj,
203 NeuterList * pOwnerList,
204 CordbHashTable *pTable,
206 RSInitHolder<CordbHashTableEnum> * pHolder)
208 CordbHashTableEnum * pEnum = new CordbHashTableEnum(pOwnerObj, pOwnerList, pTable, id);
209 pHolder->Assign(pEnum);
211 // If no neuter-list supplied, then our owner is manually managing us.
212 // It also means we can't be cloned.
213 if (pOwnerList != NULL)
215 pOwnerList->Add(pOwnerObj->GetProcess(), pEnum);
219 pEnum->m_DbgChangeCount = pEnum->m_table->GetChangeCount();
224 // Copy constructor makes life easy & fun!
225 CordbHashTableEnum::CordbHashTableEnum(CordbHashTableEnum *cloneSrc)
226 : CordbBase(cloneSrc->m_pOwnerObj->GetProcess(), 0, enumCordbHashTableEnum),
227 m_pOwnerObj(cloneSrc->m_pOwnerObj),
228 m_pOwnerNeuterList(cloneSrc->m_pOwnerNeuterList),
229 m_started(cloneSrc->m_started),
230 m_done(cloneSrc->m_done),
231 m_hashfind(cloneSrc->m_hashfind),
232 m_guid(cloneSrc->m_guid),
233 m_iCurElt(cloneSrc->m_iCurElt),
234 m_count(cloneSrc->m_count),
235 m_fCountInit(cloneSrc->m_fCountInit)
237 // We can get cloned at any time, so our owner can't manually control us,
238 // so we need explicit access to a neuter list.
239 _ASSERTE(m_pOwnerNeuterList != NULL);
241 m_table = cloneSrc->m_table;
246 // Add to neuter list
247 if (m_pOwnerObj->GetProcess() != NULL)
249 // Normal case. For things enumerating stuff within a CordbProcess tree.
250 m_pOwnerNeuterList->Add(m_pOwnerObj->GetProcess(), this);
254 // For Process-list enums that have broken neuter semantics.
255 // @dbgtodo: this goes away once we remove the top-level ICorDebug interface,
256 // and thus no longer have a Process enumerator.
257 m_pOwnerNeuterList->UnsafeAdd(NULL, this);
260 EX_CATCH_HRESULT(hr);
261 SetUnrecoverableIfFailed(GetProcess(), hr);
264 m_DbgChangeCount = cloneSrc->m_DbgChangeCount;
268 CordbHashTableEnum::~CordbHashTableEnum()
270 _ASSERTE(this->IsNeutered());
272 _ASSERTE(m_table == NULL);
273 _ASSERTE(m_pOwnerObj == NULL);
274 _ASSERTE(m_pOwnerNeuterList == NULL);
277 void CordbHashTableEnum::Neuter()
281 m_pOwnerNeuterList = NULL;
287 HRESULT CordbHashTableEnum::Reset()
297 HRESULT CordbHashTableEnum::Clone(ICorDebugEnum **ppEnum)
299 PUBLIC_REENTRANT_API_ENTRY(this);
300 FAIL_IF_NEUTERED(this);
303 VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorDebugEnum **);
308 CordbHashTableEnum *e = NULL;
310 CordbProcess * pProc = GetProcess();
314 // @todo - this is really ugly. This macro sets up stack-based dtor cleanup
315 // objects, and so it has to be in the same block of code as the code
316 // it protectes. Eg, we couldn't say: 'if (...) { ATT_ } { common code }'
317 // because the ATT_ stack based object would get destructed before the 'common code'
319 ATT_REQUIRE_STOPPED_MAY_FAIL(pProc);
320 e = new (nothrow) CordbHashTableEnum(this);
324 e = new (nothrow) CordbHashTableEnum(this);
335 e->QueryInterface(m_guid, (void **) ppEnum);
341 HRESULT CordbHashTableEnum::GetCount(ULONG *pcelt)
343 PUBLIC_REENTRANT_API_ENTRY(this);
344 FAIL_IF_NEUTERED(this);
347 VALIDATE_POINTER_TO_OBJECT(pcelt, ULONG *);
349 *pcelt = m_table->GetCount();
354 HRESULT CordbHashTableEnum::PrepForEnum(CordbBase **pBase)
360 (*pBase) = m_table->UnsafeUnlockedFindFirst(&m_hashfind);
365 (*pBase) = m_table->UnsafeUnlockedFindNext(&m_hashfind);
371 HRESULT CordbHashTableEnum::SetupModuleEnum(void)
377 HRESULT CordbHashTableEnum::AdvancePreAssign(CordbBase **pBase)
383 HRESULT CordbHashTableEnum::AdvancePostAssign(CordbBase **pBase,
392 // If we're looping like normal, or we're in skip
393 if ( ((b < bEnd) || ((b ==bEnd)&&(b==NULL)))
396 (*pBase) = m_table->UnsafeUnlockedFindNext(&m_hashfind);
404 // This is an public function implementing all of the ICorDebugXXXEnum interfaces.
405 HRESULT CordbHashTableEnum::Next(ULONG celt,
409 PUBLIC_REENTRANT_API_ENTRY(this);
410 FAIL_IF_NEUTERED(this);
413 VALIDATE_POINTER_TO_OBJECT_ARRAY(bases,
418 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pceltFetched,
421 if ((pceltFetched == NULL) && (celt != 1))
428 if (pceltFetched != NULL)
436 CordbBase *base = NULL;
437 CordbBase **b = bases;
438 CordbBase **bEnd = bases + celt;
440 hr = PrepForEnum(&base);
446 while (b < bEnd && !m_done)
448 hr = AdvancePreAssign(&base);
460 if (m_guid == IID_ICorDebugProcessEnum)
462 *b = (CordbBase*)(ICorDebugProcess*)(CordbProcess*)base;
464 else if (m_guid == IID_ICorDebugBreakpointEnum)
466 *b = (CordbBase*)(ICorDebugBreakpoint*)(CordbBreakpoint*)base;
468 else if (m_guid == IID_ICorDebugStepperEnum)
470 *b = (CordbBase*)(ICorDebugStepper*)(CordbStepper*)base;
472 else if (m_guid == IID_ICorDebugModuleEnum)
474 *b = (CordbBase*)(ICorDebugModule*)(CordbModule*)base;
476 else if (m_guid == IID_ICorDebugThreadEnum)
478 *b = (CordbBase*)(ICorDebugThread*)(CordbThread*)base;
480 else if (m_guid == IID_ICorDebugAppDomainEnum)
482 *b = (CordbBase*)(ICorDebugAppDomain*)(CordbAppDomain*)base;
484 else if (m_guid == IID_ICorDebugAssemblyEnum)
486 *b = (CordbBase*)(ICorDebugAssembly*)(CordbAssembly*)base;
490 *b = (CordbBase*)(IUnknown*)base;
495 // 'b' is not a valid CordbBase ptr.
496 base->ExternalAddRef();
500 hr = AdvancePostAssign(&base, b, bEnd);
510 // If celt == 1, then the pceltFetched parameter is optional.
512 if (pceltFetched != NULL)
514 *pceltFetched = (ULONG)(b - bases);
518 // If we reached the end of the enumeration, but not the end
519 // of the number of requested items, we return S_FALSE.
521 if (!FAILED(hr) && m_done && (b != bEnd))
529 HRESULT CordbHashTableEnum::Skip(ULONG celt)
531 PUBLIC_REENTRANT_API_ENTRY(this);
532 FAIL_IF_NEUTERED(this);
543 base = m_table->UnsafeUnlockedFindFirst(&m_hashfind);
553 while (celt > 0 && !m_done)
555 base = m_table->UnsafeUnlockedFindNext(&m_hashfind);
567 HRESULT CordbHashTableEnum::QueryInterface(REFIID id, void **pInterface)
569 if (id == IID_ICorDebugEnum)
572 *pInterface = static_cast<ICorDebugEnum *>(static_cast<ICorDebugProcessEnum *>(this));
576 if (id == IID_IUnknown)
579 *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugProcessEnum *>(this));
587 if (id == IID_ICorDebugProcessEnum)
588 *pInterface = static_cast<ICorDebugProcessEnum *>(this);
589 else if (id == IID_ICorDebugBreakpointEnum)
590 *pInterface = static_cast<ICorDebugBreakpointEnum *>(this);
591 else if (id == IID_ICorDebugStepperEnum)
592 *pInterface = static_cast<ICorDebugStepperEnum *>(this);
593 else if (id == IID_ICorDebugModuleEnum)
594 *pInterface = static_cast<ICorDebugModuleEnum *>(this);
595 else if (id == IID_ICorDebugThreadEnum)
596 *pInterface = static_cast<ICorDebugThreadEnum *>(this);
597 else if (id == IID_ICorDebugAppDomainEnum)
598 *pInterface = static_cast<ICorDebugAppDomainEnum *>(this);
599 else if (id == IID_ICorDebugAssemblyEnum)
600 *pInterface = static_cast<ICorDebugAssemblyEnum *>(this);
605 return E_NOINTERFACE;
609 void CordbHashTableEnum::AssertValid()
611 // @todo - Our behavior is undefined when enumerating a collection that changes underneath us.
612 // We'd love to just call this situatation illegal, but clients could very reasonably taken a depedency
613 // on it. Various APIs (eg, ICDStepper::Deactivate) may remove items from the hash.
614 // So we need to figure out what the behavior is here, spec it, and then enforce that and enable the
615 // strongest asserts possible there.
617 // Specifically, we cannot check that the hash hasn't change from underneath us:
618 // CONSISTENCY_CHECK_MSGF(m_DbgChangeCount == m_table->GetChangeCount(),
619 // ("Underlying hashtable has changed while enumerating.\nOriginal stamp=%d\nNew stamp=%d\nThis enum=0x%p",
620 // m_DbgChangeCount, m_table->GetChangeCount(), this));
625 void CordbHashTable::AssertIsProtected()
628 if (m_pDbgLock != NULL)
630 DbgRSThread * pThread = DbgRSThread::GetThread();
631 if (pThread->IsInRS())
633 CONSISTENCY_CHECK_MSGF(m_pDbgLock->HasLock(), ("Hash table being accessed w/o holding '%s'", m_pDbgLock->Name()));