windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/infocomm/atq/dpte.hxx
2020-09-26 16:20:57 +08:00

302 lines
7.5 KiB
C++

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name :
dpte.hxx
Abstract:
This module determines the size of transfer chunks to be used when
calling TransmitFile()
Author:
Bilal Alam ( t-bilala ) 9-April-1997
Environment:
User Mode -- Win32
Project:
Internet Services Asynchronous Thread Queue DLL
--*/
#ifndef DPTE_HXX_INCLUDED
#define DPTE_HXX_INCLUDED
#ifndef PAGE_SIZE
#define MINIMUM_CHUNK_SIZE 4096
#else
#define MINIMUM_CHUNK_SIZE PAGE_SIZE
#endif
#define DEFAULT_CHUNK_SIZE 65536
#define ROUND_CHUNK_SIZE( cb ) ( (((cb) + 512)/1024) * 1024)
/* ChunkAlgorithms
The goal is to improve upon the existing behaviour where TransmitFile()
calls set the nNumberOfBytesPerSend = 0. This (for NT server) causes
64K to be used for each TransmitFile() and this is problematic for slow
links since huge amounts of memory are kept in use. To address this
problem, a ChunkAlgorithm is used to dynamically set the chunk size for
the TransmitFile() call. The policy for each algorithm determines when
to decrease the chunk size and when to increase it again.
*/
/* Plugging this stuff in
- In the registry, algorithm number and parameter are set (default case
is DefaultChunkAlgorithm,0 )
- Construct appropriate chunk algorithm (global to ATQ)
- In the ATQ timeout call, call Evaluate() method of chunk algorithm
- Before doing TransmitFile() call QueryChunkSize() to get chunk size to
pass into TransmitFile()
- For SPUDTransmitFileAndRecv(), set the
AFD_TRANSMIT_FILE_INFO.SendPacketLength member appropriately.
- Store the chunk size in the ATQ context so that when the TransmitFile()
completes, you can appropriately update the statistics (if necessary)
through the DecreaseUsage() method.
*/
/* ChunkAlgorithm is base class from which all chunksize algorithms are
derived.
*/
class ChunkAlgorithm
{
public:
ChunkAlgorithm( DWORD dwSize = DEFAULT_CHUNK_SIZE ) :
_cbChunkSize( dwSize )
{
}
virtual ~ChunkAlgorithm( VOID )
{
}
// SetParameter()
//
// Used to set parameters for use by the chunk algorithms.
virtual BOOL SetParameter( DWORD dwParmNumber, DWORD dwValue ) = 0;
// Evaluate()
//
// This is called every ATQ_TIMEOUT to re-evaluate the chunk size and if
// necessary, change the size for subsequent requests.
virtual VOID Evaluate( VOID ) = 0;
// IncreaseUsage()
//
// When a TransmitFile() call is about to occur, ATQ will call this
// method to update the statistic maintaining current PTE usage. For
// algorithms that don't keep statistics, the virtual function will be
// a NOP.
virtual VOID IncreaseUsage( DWORD cbUsage ) = 0;
// DecreaseUsage()
//
// When a TransmitFile() call has completed, ATQ will call this
// method to update the statistic maintaining current PTE usage. For
// algorithms that don't keep statistics, the virtual function will be
// a NOP.
virtual VOID DecreaseUsage( DWORD cbUsage ) = 0;
// QueryChunkSize()
//
// Before calling TransmitFile() this function will be called to get
// the chunk size to be passed into TransmitFile().
DWORD QueryChunkSize( VOID )
{
return _cbChunkSize;
}
protected:
DWORD _cbChunkSize;
};
/* DefaultAlgorithm sets the chunksize for TransmitFile() to be a constant.
By default, this is the algorithm used with the fixed value being 0.
(this default case is the original behaviour of ATQ). The fixed value
can be non-zero in which case all TransmitFile() call will use this value
as the chunk size.
*/
class DefaultAlgorithm : public ChunkAlgorithm
{
public:
DefaultAlgorithm( VOID ) : ChunkAlgorithm( 0 )
{
}
~DefaultAlgorithm( VOID )
{
}
BOOL SetParameter( DWORD dwParmNumber, DWORD dwValue )
{
ATQ_ASSERT( dwParmNumber == 1 );
if ( dwParmNumber == 1 )
{
_cbChunkSize = dwValue;
return TRUE;
}
else
{
return FALSE;
}
}
VOID IncreaseUsage( DWORD cbUsage )
{
}
VOID DecreaseUsage( DWORD cbUsage )
{
}
VOID Evaluate( VOID )
{
}
};
/* DivideAlgorithm keeps statistics on the current amount of PTE usage and
compares this to a given threshold value. The closer the amount gets to
the threshold, the lower the chunk size becomes. Beware of this algorithm
since it actually takes statistics and thus significantly can slow down
the mainline code path for TransmitFile().
*/
class DivideAlgorithm : public ChunkAlgorithm
{
public:
DivideAlgorithm( VOID )
{
}
~DivideAlgorithm( VOID )
{
}
BOOL SetParameter( DWORD dwParmNumber, DWORD dwValue )
{
ATQ_ASSERT( dwParmNumber == 1 );
if ( dwParmNumber == 1 )
{
_cbThreshold = dwValue;
return TRUE;
}
else
{
return FALSE;
}
}
VOID Evaluate( VOID )
{
DWORD cbCurrentUsage = _cbCurrentUsage;
DWORD cbFree = _cbThreshold - cbCurrentUsage;
if ( _cbThreshold > cbCurrentUsage )
{
_cbChunkSize =
ROUND_CHUNK_SIZE( DEFAULT_CHUNK_SIZE / ( _cbThreshold / cbFree ) );
if ( _cbChunkSize < MINIMUM_CHUNK_SIZE )
{
_cbChunkSize = MINIMUM_CHUNK_SIZE;
}
}
else
{
_cbChunkSize = MINIMUM_CHUNK_SIZE;
}
}
VOID IncreaseUsage( DWORD cbUsage )
{
InterlockedExchangeAdd( (LPLONG) &_cbCurrentUsage, (LONG) cbUsage );
}
VOID DecreaseUsage( DWORD cbUsage )
{
InterlockedExchangeAdd( (LPLONG) &_cbCurrentUsage, ( (LONG) cbUsage ) * -1 );
}
private:
DWORD _cbCurrentUsage;
DWORD _cbThreshold;
};
/* MemoryLoadAlgorithm uses the GlobalMemoryStatus() function to determine
what the chunk size should be for TransmitFile(). This API call will return
(amongst other things) a load value from 0-100. This value is used to
set the chunk size.
*/
class MemoryLoadAlgorithm : public ChunkAlgorithm
{
public:
MemoryLoadAlgorithm( VOID )
{
rChunkSizeTable[ 0 ] = DEFAULT_CHUNK_SIZE;
rChunkSizeTable[ 1 ] = DEFAULT_CHUNK_SIZE;
rChunkSizeTable[ 2 ] = DEFAULT_CHUNK_SIZE;
rChunkSizeTable[ 3 ] = DEFAULT_CHUNK_SIZE;
rChunkSizeTable[ 4 ] = DEFAULT_CHUNK_SIZE >> 1;
rChunkSizeTable[ 5 ] = DEFAULT_CHUNK_SIZE >> 2;
rChunkSizeTable[ 6 ] = DEFAULT_CHUNK_SIZE >> 3;
rChunkSizeTable[ 7 ] = DEFAULT_CHUNK_SIZE >> 4;
rChunkSizeTable[ 8 ] = MINIMUM_CHUNK_SIZE;
rChunkSizeTable[ 9 ] = MINIMUM_CHUNK_SIZE;
rChunkSizeTable[ 10] = MINIMUM_CHUNK_SIZE;
}
~MemoryLoadAlgorithm( VOID )
{
}
BOOL SetParameter( DWORD dwParmNumber, DWORD dwValue )
{
return TRUE;
}
VOID Evaluate( VOID )
{
MEMORYSTATUS memStatus;
GlobalMemoryStatus( &memStatus );
InterlockedExchange( (LPLONG) &_cbChunkSize,
rChunkSizeTable[ memStatus.dwMemoryLoad / 10 ] );
}
VOID IncreaseUsage( DWORD cbUsage )
{
}
VOID DecreaseUsage( DWORD cbUsage )
{
}
private:
DWORD rChunkSizeTable[ 11 ];
};
#endif