396 lines
12 KiB
C++
396 lines
12 KiB
C++
|
/*++
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|