|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
cache.c
Abstract:
This module contains the code to cache BINL client information across requests for the BINL server.
Author:
Andy Herron (andyhe) 5-Mar-1998
Environment:
User Mode - Win32
Revision History:
--*/
#include "binl.h"
#pragma hdrstop
ULONG BinlCacheCount = 0; ULONG BinlCacheEntriesInUse = 0;
VOID BinlFreeCacheEntry ( PMACHINE_INFO CacheEntry );
DWORD BinlCreateOrFindCacheEntry ( PCHAR Guid, BOOLEAN CreateIfNotExist, PMACHINE_INFO *CacheEntry ) //
// This searches the list of cached entries for the matching GUID. If it is
// found and is in use, we return an error as another thread is working on
// the same request. If it is found and not in use, we mark it in use and
// return it. If it is not found, we add it and return it.
//
{ PLIST_ENTRY listEntry; PMACHINE_INFO currentEntry = NULL; DWORD expireTickCount;
EnterCriticalSection( &BinlCacheListLock );
//
// For now, we don't bother with a scavenger thread. Just free them up
// as we come across them.
//
if (BinlCacheExpireMilliseconds > 0) {
expireTickCount = GetTickCount() - BinlCacheExpireMilliseconds;
} else {
expireTickCount = 0; }
if (BinlCurrentState == BINL_STOPPED) {
//
// if we're in the midst of shutting down, ignore the request. We
// do the check while holding the lock to synchronize with any thread
// calling BinlCloseCache.
//
LeaveCriticalSection( &BinlCacheListLock ); *CacheEntry = NULL;
//
// We return the EVENT_SERVER_SHUTDOWN to tell the calling thread
// not to bother with this request.
//
return EVENT_SERVER_SHUTDOWN; }
listEntry = BinlCacheList.Flink;
while ( listEntry != &BinlCacheList ) {
LONG isEqual;
currentEntry = (PMACHINE_INFO) CONTAINING_RECORD( listEntry, MACHINE_INFO, CacheListEntry );
//
// lazy free.. check to see if we should free this entry because
// it's time to live has expired.
//
if ((currentEntry->InProgress == FALSE) && (expireTickCount > 0) && (currentEntry->TimeCreated < expireTickCount)) {
listEntry = listEntry->Flink; BinlFreeCacheEntry( currentEntry );
BinlPrintDbg((DEBUG_BINL_CACHE, "removed cache entry %x", currentEntry )); continue; }
//
// search for the given guid. The list is sorted by GUID so when
// we hit a guid that is greater than current, we stop searching.
//
isEqual = memcmp( Guid, currentEntry->Guid, BINL_GUID_LENGTH );
if (isEqual < 0) {
listEntry = listEntry->Flink; continue; }
if (isEqual == 0) {
DWORD rc = ERROR_SUCCESS;
//
// If another thread is using this entry, then we should ignore
// the request we're currently working on as the other thread will
// respond.
if (currentEntry->InProgress == TRUE) {
LeaveCriticalSection( &BinlCacheListLock ); *CacheEntry = NULL; return ERROR_BINL_CLIENT_EXISTS; }
//
// Also, if the entry is not ours to handle, then we return the
// error here. We don't return ERROR_BINL_INVALID_BINL_CLIENT
// because that will tell GetBootParameters to process it as a
// new client. Instead, we return ERROR_BINL_CLIENT_EXISTS so
// that the caller will simply return the error. A bit ugly,
// but necessary.
//
if (currentEntry->MyClient == FALSE) {
if (currentEntry->EntryExists) { LeaveCriticalSection( &BinlCacheListLock ); *CacheEntry = NULL; return ERROR_BINL_CLIENT_EXISTS; } //
// we return the empty entry since we might now be
// creating the account.
//
rc = ERROR_BINL_INVALID_BINL_CLIENT; }
//
// since we're now using an entry, reset the event saying all
// threads are done.
//
BinlCacheEntriesInUse++;
currentEntry->InProgress = TRUE; *CacheEntry = currentEntry;
LeaveCriticalSection( &BinlCacheListLock );
return rc; }
//
// we're at the first entry that is greater than the guid in question.
//
break; }
if (! CreateIfNotExist) {
LeaveCriticalSection( &BinlCacheListLock ); *CacheEntry = NULL; return ERROR_BINL_INVALID_BINL_CLIENT; }
// currentEntry is not valid, but listEntry is.
//
// Add a new entry at the end of listEntry. Either listEntry points to
// the first entry that is larger than our guid or it points to the root
// of the list (in which case we add it at the end).
//
currentEntry = BinlAllocateMemory( sizeof( MACHINE_INFO ) );
if (currentEntry == NULL) {
*CacheEntry = NULL; LeaveCriticalSection( &BinlCacheListLock ); return ERROR_NOT_ENOUGH_MEMORY; }
currentEntry->TimeCreated = GetTickCount(); currentEntry->InProgress = TRUE;
// leave MyClient and EntryExists as FALSE
memcpy( currentEntry->Guid, Guid, BINL_GUID_LENGTH ); InsertTailList( listEntry, ¤tEntry->CacheListEntry ); InitializeListHead( ¤tEntry->DNsWithSameGuid );
BinlCacheEntriesInUse++; BinlCacheCount++; *CacheEntry = currentEntry;
//
// If we're at the max, then go through the entire list to free the
// oldest. We do this here because the loop below doesn't go through
// the whole list.
//
if (BinlCacheCount > BinlGlobalCacheCountLimit) {
PMACHINE_INFO entryToDelete = NULL; DWORD earliestTime;
listEntry = BinlCacheList.Flink;
while ( listEntry != &BinlCacheList ) {
currentEntry = (PMACHINE_INFO) CONTAINING_RECORD( listEntry, MACHINE_INFO, CacheListEntry );
listEntry = listEntry->Flink;
if (currentEntry->InProgress == FALSE) {
//
// if this one is expired, stop here as we have one to free
//
if ((expireTickCount > 0) && (currentEntry->TimeCreated < expireTickCount)) {
entryToDelete = currentEntry; break; }
//
// if this one is earlier than the one we previously found,
// remember it.
//
if ((( entryToDelete == NULL) || ( currentEntry->TimeCreated < earliestTime)) ) {
entryToDelete = currentEntry; earliestTime = currentEntry->TimeCreated; } } } if (entryToDelete) { BinlFreeCacheEntry( entryToDelete ); BinlPrintDbg((DEBUG_BINL_CACHE, "removed cache entry %x", entryToDelete )); } }
LeaveCriticalSection( &BinlCacheListLock );
return ERROR_SUCCESS; }
VOID BinlDoneWithCacheEntry ( PMACHINE_INFO CacheEntry, BOOLEAN FreeIt ) { EnterCriticalSection( &BinlCacheListLock );
//
// This one is no longer actively used. See if it's time to set the
// event to tell the terminating thread that everyone is done.
//
CacheEntry->InProgress = FALSE;
BinlCacheEntriesInUse--;
if ((BinlCacheEntriesInUse == 0) && BinlCloseCacheEvent) {
SetEvent( BinlCloseCacheEvent ); }
if (FreeIt) {
BinlFreeCacheEntry( CacheEntry ); }
LeaveCriticalSection( &BinlCacheListLock );
BinlPrintDbg((DEBUG_BINL_CACHE, "binl done with cache entry 0x%x, FreeIt == %s\n", CacheEntry, (FreeIt == TRUE) ? "TRUE" : "FALSE" )); return; }
VOID BinlFreeCacheEntry ( PMACHINE_INFO CacheEntry ) //
// The lock must be held while coming in here. It will be not be freed.
//
{ PLIST_ENTRY p; PDUP_GUID_DN dupDN;
//
// We're done with this entry. Simply remove it from the list, free it,
// and update the global count. The lock is held, so party on.
//
BinlPrintDbg((DEBUG_BINL_CACHE, "binl freeing cache entry at 0x%x\n", CacheEntry ));
RemoveEntryList( &CacheEntry->CacheListEntry );
if ( CacheEntry->dwFlags & MI_NAME_ALLOC ) { BinlFreeMemory( CacheEntry->Name ); } if ( CacheEntry->dwFlags & MI_SETUPPATH_ALLOC ) { BinlFreeMemory( CacheEntry->SetupPath ); } if ( CacheEntry->dwFlags & MI_HOSTNAME_ALLOC ) { BinlFreeMemory( CacheEntry->HostName ); } if ( CacheEntry->dwFlags & MI_BOOTFILENAME_ALLOC ) { BinlFreeMemory( CacheEntry->BootFileName ); } if ( CacheEntry->dwFlags & MI_SAMNAME_ALLOC ) { BinlFreeMemory( CacheEntry->SamName ); } if ( CacheEntry->dwFlags & MI_DOMAIN_ALLOC ) { BinlFreeMemory( CacheEntry->Domain ); } if ( CacheEntry->dwFlags & MI_SIFFILENAME_ALLOC ) { BinlFreeMemory( CacheEntry->ForcedSifFileName ); } if ( CacheEntry->dwFlags & MI_MACHINEDN_ALLOC ) { BinlFreeMemory( CacheEntry->MachineDN ); }
while (!IsListEmpty(&CacheEntry->DNsWithSameGuid)) {
p = RemoveHeadList(&CacheEntry->DNsWithSameGuid);
dupDN = CONTAINING_RECORD(p, DUP_GUID_DN, ListEntry); BinlFreeMemory( dupDN ); }
BinlFreeMemory( CacheEntry ); BinlCacheCount--;
return; }
VOID BinlCloseCache ( VOID ) //
// This routine closes down all entries in the DS cache. It waits until all
// threads are done with entries before it returns. It waits for the
// BinlCloseCacheEvent to be set if threads are waiting.
//
{ PLIST_ENTRY listEntry;
EnterCriticalSection( &BinlCacheListLock );
listEntry = BinlCacheList.Flink;
while ( listEntry != &BinlCacheList ) {
DWORD Error;
PMACHINE_INFO cacheEntry;
//
// For each entry in the list, if it's not in use we free it. If it
// is in use, we wait for the thread to finish with it.
//
cacheEntry = (PMACHINE_INFO) CONTAINING_RECORD( listEntry, MACHINE_INFO, CacheListEntry );
if (cacheEntry->InProgress != TRUE) {
listEntry = listEntry->Flink; BinlFreeCacheEntry( cacheEntry ); continue; }
if (BinlCloseCacheEvent) {
ResetEvent( BinlCloseCacheEvent ); }
LeaveCriticalSection( &BinlCacheListLock );
//
// Wait for the event signalling that all threads are done with
// the cache
//
if (BinlCloseCacheEvent) {
Error = WaitForSingleObject( BinlCloseCacheEvent, THREAD_TERMINATION_TIMEOUT );
} else {
//
// well, the event that we would wait on isn't there and there's
// still a worker thread using a cache entry, so we just wait
// and then recheck. Yup, this is ugly.
//
Sleep( 10*1000 ); }
EnterCriticalSection( &BinlCacheListLock ); listEntry = BinlCacheList.Flink; }
LeaveCriticalSection( &BinlCacheListLock ); }
// cache.c eof
|