/*++ Copyright (c) 1996 Microsoft Corporation Module Name : rhash.cxx Abstract: This file contains type definitions hash table support Author: Revision History: Nimish Khanolkar (NimishK) --*/ #include "precomp.h" CRETRY_HASH_TABLE::CRETRY_HASH_TABLE() { TraceFunctEnterEx((LPARAM)this, "CRETRY_HASH_TABLE::CRETRY_HASH_TABLE"); m_TotalEntries = 0; m_Signature = RETRY_TABLE_SIGNATURE_VALID; TraceFunctLeaveEx((LPARAM)this); } CRETRY_HASH_TABLE::~CRETRY_HASH_TABLE() { TraceFunctEnterEx((LPARAM)this, "CRETRY_HASH_TABLE::~CRETRY_HASH_TABLE"); m_Signature = RETRY_TABLE_SIGNATURE_FREE; TraceFunctLeaveEx((LPARAM)this); } ///////////////////////////////////////////////////////////////////////////////// // CRETRY_HASH_TABLE::DeInitialize // // Very Basic. // By this time there should be no threads adding anything to the hash table // Frees up all the entries in the hash table // ///////////////////////////////////////////////////////////////////////////////// HRESULT CRETRY_HASH_TABLE::DeInitialize(void) { //Remove all the entries from the HashTable RemoveAllEntries(); delete this; return S_OK; } ///////////////////////////////////////////////////////////////////////////////// // CRETRY_HASH_TABLE::RemoveThisEntry // // Need to add code to remove entry from retry q as well // ///////////////////////////////////////////////////////////////////////////////// void CRETRY_HASH_TABLE::RemoveThisEntry(CRETRY_HASH_ENTRY * pHashEntry, DWORD BucketNum) { TraceFunctEnterEx((LPARAM)this, "CRETRY_HASH_TABLE::RemoveThisEntry"); DebugTrace((LPARAM)this, "removing %s from hash table", pHashEntry->GetHashKey()); m_HashTable[BucketNum].m_Lock.ExclusiveLock(); RemoveEntryList(&pHashEntry->QueryHListEntry()); m_HashTable[BucketNum].m_Lock.ExclusiveUnlock(); TraceFunctLeaveEx((LPARAM)this); } ///////////////////////////////////////////////////////////////////////////////// // CRETRY_HASH_TABLE::RemoveAllEntries // // To be clean : Need to add code to remove each entry from retry q as well // But not really needed as I will not be leaking anything if I simply destroy the // queue // ///////////////////////////////////////////////////////////////////////////////// void CRETRY_HASH_TABLE::RemoveAllEntries(void) { DWORD LoopVar = 0; PLIST_ENTRY HeadOfList = NULL; PLIST_ENTRY pEntry = NULL; CRETRY_HASH_ENTRY * pHashEntry = NULL; TraceFunctEnterEx((LPARAM)this, "CRETRY_HASH_TABLE::RemoveAllEntries"); for (LoopVar = 0; LoopVar < TABLE_SIZE; LoopVar++) { m_HashTable[LoopVar].m_Lock.ExclusiveLock(); HeadOfList = &m_HashTable[LoopVar].m_ListHead; while (!IsListEmpty(HeadOfList)) { //remove the entries from the list pEntry = RemoveHeadList (HeadOfList); pHashEntry = CONTAINING_RECORD(pEntry, CRETRY_HASH_ENTRY, m_HLEntry); _ASSERT(pHashEntry->IsValid()); DebugTrace((LPARAM)this, "removing %s from hash table. RefCnt = %d", pHashEntry->GetHashKey(), pHashEntry->QueryRefCount()); pHashEntry->ClearInTable(); pHashEntry->DecRefCount(); //decrement entry counts m_HashTable[LoopVar].m_NumEntries--; InterlockedIncrement(&(m_HashTable[LoopVar].m_RefNum)); InterlockedDecrement(&m_TotalEntries); } m_HashTable[LoopVar].m_Lock.ExclusiveUnlock(); } TraceFunctLeaveEx((LPARAM)this); } ///////////////////////////////////////////////////////////////////////////////// // CRETRY_HASH_TABLE::PrintAllEntries // // Should not use this one - not really useful. // Instead Use RETRYQ::PrintAllQEntries() // ///////////////////////////////////////////////////////////////////////////////// // void CRETRY_HASH_TABLE::PrintAllEntries(void) { DWORD LoopVar = 0; PLIST_ENTRY HeadOfList = NULL; PLIST_ENTRY pEntry = NULL; PLIST_ENTRY pentryNext = NULL; CRETRY_HASH_ENTRY * pHashEntry = NULL; for (LoopVar = 0; LoopVar < TABLE_SIZE; LoopVar++) { m_HashTable[LoopVar].m_Lock.ExclusiveLock(); HeadOfList = &m_HashTable[LoopVar].m_ListHead; pEntry = m_HashTable[LoopVar].m_ListHead.Flink; for(; pEntry != HeadOfList; pEntry = pentryNext) { pentryNext = pEntry->Flink; pHashEntry = CONTAINING_RECORD(pEntry, CRETRY_HASH_ENTRY, m_HLEntry); printf("%s i n bucket %d\n", pHashEntry->GetHashKey(), LoopVar); } m_HashTable[LoopVar].m_Lock.ExclusiveUnlock(); } } ///////////////////////////////////////////////////////////////////////////////// // CRETRY_HASH_TABLE::InsertIntoTable // // We insert a new entry into the table // If we find an existing one for the same domain we remove it and add this new // one // Adding an entry to the Hash Table also results in adding the entry to the retry // queue ordered on the RetryTime // ///////////////////////////////////////////////////////////////////////////////// // BOOL CRETRY_HASH_TABLE::InsertIntoTable(CRETRY_HASH_ENTRY * pHashEntry) { unsigned int HashValue = 0; char * NewData = NULL; char * ExistingData = NULL; PLIST_ENTRY HeadOfList = NULL; PLIST_ENTRY ListEntry =NULL; CRETRY_HASH_ENTRY * pExistingEntry = NULL; int Result = 0; TraceFunctEnterEx((LPARAM)this, "CRETRY_HASH_TABLE::InsertIntoTable"); _ASSERT(pHashEntry != NULL); if(pHashEntry == NULL) { TraceFunctLeaveEx((LPARAM)this); return FALSE; } //get the new key NewData = pHashEntry->GetHashKey(); _ASSERT(NewData != NULL); if(NewData == NULL) { TraceFunctLeaveEx((LPARAM)this); return FALSE; } CharLowerBuff((char *)NewData, lstrlen(NewData)); //get the hash value for it HashValue = HashFunction (NewData); //lock the list exclusively m_HashTable[HashValue].m_Lock.ExclusiveLock(); //insert the head of the list for this bucket //duplicates are dealt by removing the dup entry and //adding the new one HeadOfList = &m_HashTable[HashValue].m_ListHead; for (ListEntry = HeadOfList->Flink; ListEntry != HeadOfList; ListEntry = ListEntry->Flink) { _ASSERT(ListEntry != NULL); pExistingEntry = CONTAINING_RECORD(ListEntry, CRETRY_HASH_ENTRY, m_HLEntry); if(pExistingEntry && pExistingEntry->IsValid()) { //So we got a valid entry ExistingData = pExistingEntry->GetHashKey(); } else { m_HashTable[HashValue].m_Lock.ExclusiveUnlock(); SetLastError(ERROR_INVALID_DATA); //We have a corrupt entry in the hash table DebugTrace((LPARAM)this, "hash table bucket %d has a currupt entry listEntry ", HashValue); _ASSERT(pExistingEntry->IsValid()); TraceFunctLeaveEx((LPARAM)this); return FALSE; } _ASSERT(ExistingData != NULL); Result = lstrcmpi(NewData, ExistingData); if(Result < 0) { break; } else if(Result == 0) { //So there is already a Hash Entry DebugTrace((LPARAM)this, "%s is already in hash table ", pHashEntry->GetHashKey()); //If we already have an entry in there - we will live with it //For the time being, we will taking the approach that if something //makes us establish a new connection before the retry interval and //we fail the connection, then we simply ignore the failure and stick //with the original retry interval //eg at 2:00pm we inserted something for retry at 2:30pm // at 2:20pm something triggers this domain, but the connection fails // Instead of updating the retry queue to now retry at 2:50 pm, // we will instead retry at 2:30pm as orginally planned. //unlock this bucket m_HashTable[HashValue].m_Lock.ExclusiveUnlock(); SetLastError(ERROR_FILE_EXISTS); TraceFunctLeaveEx((LPARAM)this); return FALSE; } } // Ok, insert the entry here here. //inc the ref count on the hash entry pHashEntry->IncRefCount(); //We just added the entry the ref count cannot be anything but 1 _ASSERT(pHashEntry->QueryRefCount() == 1); _ASSERT(ListEntry != NULL); //Insert into the hash bucket list before the exisitng entry InsertTailList(ListEntry, &pHashEntry->QueryHListEntry()); pHashEntry->SetInTable(); //update total entries in this bucket m_HashTable[HashValue].m_NumEntries++; DebugTrace((LPARAM)this, "inserted %s into hash table. RefCnt = %d", pHashEntry->GetHashKey(), pHashEntry->QueryRefCount()); //update numentries in this bucket InterlockedIncrement(&m_TotalEntries); //unlock this bucket m_HashTable[HashValue].m_Lock.ExclusiveUnlock(); TraceFunctLeaveEx((LPARAM)this); return TRUE; } ///////////////////////////////////////////////////////////////////////////////// // CRETRY_HASH_TABLE::RemoveFromTable // // We insert a new entry into the table // If we find an existing one for the same domain we remove it and add this new // one // Adding an entry to the Hash Table also results in adding the entry to the retry // queue ordered on the RetryTime // ///////////////////////////////////////////////////////////////////////////////// BOOL CRETRY_HASH_TABLE::RemoveFromTable(const char * SearchData, PRETRY_HASH_ENTRY *ppExistingEntry) { unsigned int HashValue; char * ExistingData = NULL; PLIST_ENTRY HeadOfList; PLIST_ENTRY ListEntry; int Result = 0; TraceFunctEnterEx((LPARAM)this, "CRETRY_HASH_TABLE::RemoveFromTable"); _ASSERT(SearchData != NULL); if(SearchData == NULL) { return FALSE; } CharLowerBuff((char *)SearchData, lstrlen(SearchData)); //get the hash value HashValue = HashFunction (SearchData); m_HashTable[HashValue].m_Lock.ExclusiveLock(); HeadOfList = &m_HashTable[HashValue].m_ListHead; for (ListEntry = HeadOfList->Flink; ListEntry != HeadOfList; ListEntry = ListEntry->Flink) { _ASSERT(ListEntry != NULL); *ppExistingEntry = CONTAINING_RECORD(ListEntry, CRETRY_HASH_ENTRY, m_HLEntry); //ExistingData = (*ppExistingEntry)->GetHashKey(); if((*ppExistingEntry) && (*ppExistingEntry)->IsValid()) { //So we got a valid entry ExistingData = (*ppExistingEntry)->GetHashKey(); } else { m_HashTable[HashValue].m_Lock.ExclusiveUnlock(); SetLastError(ERROR_INVALID_DATA); //We have a corrupt entry in the hash table DebugTrace((LPARAM)this, "hash table bucket %d has a currupt entry listEntry ", HashValue); _ASSERT((*ppExistingEntry)->IsValid()); TraceFunctLeaveEx((LPARAM)this); return FALSE; } _ASSERT(ExistingData != NULL); Result = lstrcmpi(SearchData,ExistingData); if(Result == 0) { DebugTrace((LPARAM)this, "Removing %s from hash table", ExistingData); //found it //Remove it from this bucket list RemoveEntryList(ListEntry); (*ppExistingEntry)->ClearInTable(); m_HashTable[HashValue].m_NumEntries--; InterlockedDecrement(&m_TotalEntries); m_HashTable[HashValue].m_Lock.ExclusiveUnlock(); TraceFunctLeaveEx((LPARAM)this); return TRUE; } else if ( Result < 0 ) break; } //duplicates are not allowed SetLastError(ERROR_PATH_NOT_FOUND); *ppExistingEntry = NULL; m_HashTable[HashValue].m_Lock.ExclusiveUnlock(); TraceFunctLeaveEx((LPARAM)this); return FALSE; }