mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
960 lines
22 KiB
960 lines
22 KiB
/*++
|
|
|
|
Copyright (c) 1995-1996 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
ilogfile.cxx
|
|
|
|
Abstract:
|
|
This module defines helper functions for File logging
|
|
|
|
Author:
|
|
|
|
Murali R. Krishnan ( MuraliK ) 21-FEb-1996
|
|
|
|
Environment:
|
|
|
|
Win32 - User Mode
|
|
|
|
Project:
|
|
|
|
Internet Server DLL
|
|
|
|
Functions Exported:
|
|
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
/************************************************************
|
|
* Include Headers
|
|
************************************************************/
|
|
|
|
# include "tcpdllp.hxx"
|
|
# include "inetlog.h"
|
|
# include "tssched.hxx"
|
|
# include "ilogfile.hxx"
|
|
# include "ilogcls.hxx"
|
|
|
|
/************************************************************
|
|
* Functions
|
|
************************************************************/
|
|
|
|
static VOID
|
|
LogFileFlushHandler(VOID * pContext);
|
|
|
|
|
|
/************************************************************
|
|
* File Buffers
|
|
************************************************************/
|
|
|
|
# if 0
|
|
|
|
|
|
ILOG_FILE_BUFFER::ILOG_FILE_BUFFER( VOID)
|
|
: m_pbBuffer ( NULL),
|
|
m_cbUsed ( 0),
|
|
m_cbMax ( 0)
|
|
{
|
|
|
|
} // ILOG_FILE_BUFFER::ILOG_FILE_BUFFER()
|
|
|
|
|
|
|
|
ILOG_FILE_BUFFER::~ILOG_FILE_BUFFER( VOID)
|
|
{
|
|
if ( m_pbBuffer != NULL) {
|
|
|
|
TCP_FREE( m_pbBuffer);
|
|
m_pbBuffer = NULL;
|
|
}
|
|
|
|
m_cbMax = 0;
|
|
|
|
} // ILOG_FILE_BUFFER::~ILOG_FILE_BUFFER()
|
|
|
|
|
|
|
|
DWORD
|
|
ILOG_FILE_BUFFER::AllocBuffer( IN DWORD cbSize)
|
|
{
|
|
if ( m_cbMax != 0 || m_pbBuffer != NULL) {
|
|
|
|
return ( ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
m_pbBuffer = TCP_ALLOC( cbSize);
|
|
|
|
if ( m_pbBuffer == NULL) {
|
|
|
|
return ( ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
m_cbMax = cbSize;
|
|
return ( NO_ERROR);
|
|
} // ILOG_FILE_BUFFER::AllocBuffer()
|
|
|
|
|
|
|
|
# if DBG
|
|
VOID
|
|
ILOG_FILE_BUFFER::Print(VOID) const
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" ILOG_FILE_BUFFER[%08x]: buffer = %08x; %d/%d\n",
|
|
this, m_pbBuffer, m_cbUsed, m_cbMax));
|
|
return;
|
|
|
|
} // ILOG_FILE_BUFFER::Print()
|
|
|
|
# endif // DBG
|
|
|
|
|
|
# endif // 0
|
|
|
|
/************************************************************
|
|
* Log File
|
|
************************************************************/
|
|
|
|
|
|
|
|
ILOG_FILE::ILOG_FILE(VOID)
|
|
/*++
|
|
This function constructs a new File object used for handling
|
|
log files.
|
|
|
|
The reference count is used to count the number of owners
|
|
of this object. It starts off with 1.
|
|
the ILOG_FILE::OpenFileForAppend() function
|
|
inrements refcount when a new owner is given this object
|
|
the ILOG_FILE::CloseFile() function
|
|
derements refcount when an owner relinquishes the object
|
|
|
|
--*/
|
|
:
|
|
m_cReferences ( 0),
|
|
m_hFile ( INVALID_HANDLE_VALUE),
|
|
m_pvBuffer ( NULL),
|
|
m_cbBufferSize ( 0),
|
|
m_cbBufferUsed ( 0)
|
|
{
|
|
memset( m_rgchFileName, 0, sizeof(m_rgchFileName));
|
|
|
|
InitializeCriticalSection( &m_csLock);
|
|
InitializeListHead( &m_listEntry);
|
|
|
|
IF_DEBUG( INETLOG) {
|
|
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"ILOG_FILE(%08x) is created\n",
|
|
this));
|
|
}
|
|
|
|
} // ILOG_FILE::ILOG_FILE()
|
|
|
|
|
|
|
|
|
|
ILOG_FILE::~ILOG_FILE(VOID)
|
|
/*++
|
|
This function cleans up state maintained within the object -
|
|
freeing up the memory and closing file handle.
|
|
It then destroys all state information maintained.
|
|
|
|
The reference count should be zero, indicating this object is no more in use.
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( m_cReferences == 0);
|
|
|
|
Lock();
|
|
|
|
//
|
|
// 1. Close the file - close also flushes the file contents
|
|
//
|
|
|
|
DBG_REQUIRE( Close());
|
|
|
|
//
|
|
// 2. Free up buffer space
|
|
//
|
|
|
|
if ( m_pvBuffer != NULL) {
|
|
|
|
LocalFree( m_pvBuffer);
|
|
m_pvBuffer = NULL;
|
|
}
|
|
m_cbBufferSize = 0;
|
|
|
|
Unlock();
|
|
|
|
|
|
DBG_REQUIRE( m_hFile == INVALID_HANDLE_VALUE);
|
|
DBG_REQUIRE( m_pvBuffer == NULL);
|
|
DBG_REQUIRE( IsListEmpty( &m_listEntry));
|
|
DeleteCriticalSection( &m_csLock);
|
|
|
|
IF_DEBUG( INETLOG) {
|
|
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"ILOG_FILE(%08x) is destroyed\n",
|
|
this));
|
|
}
|
|
|
|
return;
|
|
} // ILOG_FILE::~ILOG_FILE()
|
|
|
|
|
|
|
|
BOOL
|
|
ILOG_FILE::Write(IN PVOID pvData, IN DWORD cbData)
|
|
/*++
|
|
This function writes the data present in the input buffer of specified
|
|
length to the file.
|
|
For performance reasons, the function actually buffers data in the
|
|
internal buffers of ILOG_FILE object. Such buffered data is flushed
|
|
to disk later on when buffer (chain) is full or when
|
|
a flush call is made from a scheduled flush.
|
|
|
|
Arguments:
|
|
pvData pointer to buffer containing data to be written
|
|
cbData count of characters of data to be written.
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is any error.
|
|
--*/
|
|
{
|
|
|
|
DWORD cbWritten = 0;
|
|
BOOL fReturn = TRUE;
|
|
BOOL fWrite = FALSE;
|
|
|
|
if ( m_hFile == INVALID_HANDLE_VALUE) {
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER);
|
|
return ( FALSE);
|
|
}
|
|
|
|
DBG_REQUIRE( m_cReferences > 0);
|
|
|
|
//
|
|
// 1. Check to see if we can write into the current buffer
|
|
// 2. If yes, write and return back
|
|
// 3. Queue up the current buffer for flushing
|
|
// 4. If a free buffer is available from freeBufferChain,
|
|
// pick it up and write data into new buffer & return
|
|
// 5. If no free buffer,
|
|
// cleanup one of the old buffer and go back to step 4.
|
|
//
|
|
// TBD:
|
|
// For Now: Do a simplified buffering
|
|
//
|
|
|
|
Lock();
|
|
|
|
DBG_ASSERT( m_hFile != INVALID_HANDLE_VALUE);
|
|
DBG_ASSERT( m_pvBuffer != NULL);
|
|
|
|
if ( m_cbBufferUsed + cbData > m_cbBufferSize) {
|
|
|
|
fReturn = Flush();
|
|
}
|
|
|
|
if ( fReturn && (m_cbBufferUsed + cbData < m_cbBufferSize)) {
|
|
|
|
//
|
|
// copy the data into the local buffer for future write
|
|
// this ensures batched writes
|
|
//
|
|
|
|
memmove(m_pvBuffer + m_cbBufferUsed, pvData, cbData);
|
|
|
|
m_cbBufferUsed += cbData;
|
|
cbWritten = cbData;
|
|
fWrite = TRUE;
|
|
}
|
|
|
|
if ( fReturn && !fWrite) {
|
|
|
|
//
|
|
// The log data is not written. Write it directly to file now.
|
|
//
|
|
// TBD: If we make sure there is a buffer always,
|
|
// then we do not need this conditional branch.
|
|
//
|
|
|
|
fReturn = WriteFile(m_hFile, pvData,
|
|
cbData,
|
|
&cbWritten, NULL);
|
|
fWrite = TRUE;
|
|
}
|
|
|
|
Unlock();
|
|
|
|
DBG_ASSERT( !fReturn || fWrite); // either failure or write succeeded
|
|
|
|
IF_DEBUG( INETLOG) {
|
|
|
|
DWORD dwError = (fReturn) ? NO_ERROR : GetLastError();
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" Wrote %d bytes out of data( %d bytes)"
|
|
" to file( %08x). Error = %d.\n",
|
|
cbWritten, cbData,
|
|
m_hFile,
|
|
dwError));
|
|
|
|
SetLastError(dwError);
|
|
}
|
|
|
|
return ( fReturn);
|
|
} // ILOG_FILE::Write()
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
ILOG_FILE::Open(IN LPCWSTR pszFileName)
|
|
/*++
|
|
This function opens up a new file for appending data.
|
|
This function automatically sets the file pointer to be
|
|
at the end of file to just enable append to file.
|
|
|
|
This function should be called after locking this object
|
|
|
|
Arguments:
|
|
pszFileName - name of the file to open
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is any failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fReturn = TRUE;
|
|
|
|
DBG_ASSERT( m_hFile == INVALID_HANDLE_VALUE);
|
|
|
|
|
|
//
|
|
// 1. Create a new file -- open a file if it already exists
|
|
//
|
|
|
|
m_hFile = CreateFileW(pszFileName,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
|
NULL, // security attributes
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL); // template file handle
|
|
|
|
fReturn = (m_hFile != INVALID_HANDLE_VALUE);
|
|
|
|
//
|
|
// 2. Set the file pointer at the end of the file (append mode)
|
|
//
|
|
|
|
if ( fReturn) {
|
|
|
|
if ( SetFilePointer( m_hFile, 0, NULL, FILE_END)
|
|
== (DWORD) -1L) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"SetFilePointer(%ws, End) failed. Error=%u\n",
|
|
pszFileName, GetLastError()));
|
|
|
|
fReturn = FALSE;
|
|
DBG_REQUIRE( Close());
|
|
m_hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 3. Allocate space for buffernig data
|
|
//
|
|
|
|
DBG_ASSERT( g_cbInetLogFileBatching > 0);
|
|
|
|
if ( fReturn && m_pvBuffer == NULL ) {
|
|
|
|
DWORD cbBuffer = g_cbInetLogFileBatching; // cache global value.
|
|
|
|
m_pvBuffer = (LPBYTE ) LocalAlloc( LPTR, cbBuffer);
|
|
|
|
if ( m_pvBuffer != NULL) {
|
|
|
|
m_cbBufferSize = cbBuffer;
|
|
m_cbBufferUsed = 0;
|
|
} else {
|
|
|
|
DBG_REQUIRE( Close());
|
|
m_hFile = INVALID_HANDLE_VALUE;
|
|
fReturn = FALSE;
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 4. Remember the name of the file on success
|
|
//
|
|
|
|
if ( fReturn) {
|
|
|
|
lstrcpynW( m_rgchFileName, pszFileName, sizeof(m_rgchFileName));
|
|
}
|
|
|
|
return ( fReturn);
|
|
} // ILOG_FILE::Open()
|
|
|
|
|
|
|
|
BOOL
|
|
ILOG_FILE::Close(VOID)
|
|
/*++
|
|
This function closes the open file. It flushes out file data
|
|
before closing the file.
|
|
|
|
This function should be called after locking this object
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is any failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fReturn = TRUE;
|
|
|
|
if ( m_hFile != INVALID_HANDLE_VALUE) {
|
|
|
|
fReturn = Flush();
|
|
|
|
FlushFileBuffers( m_hFile);
|
|
|
|
fReturn = fReturn && CloseHandle( m_hFile);
|
|
|
|
if ( fReturn) {
|
|
|
|
m_hFile = INVALID_HANDLE_VALUE; // store the invalid handle
|
|
}
|
|
}
|
|
|
|
IF_DEBUG( INETLOG) {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" ILOG_FILE::Close( %ws. Handle = %08x) returns %d."
|
|
" Error = %d\n",
|
|
m_rgchFileName,
|
|
m_hFile, fReturn,
|
|
( fReturn) ? NO_ERROR: GetLastError()));
|
|
}
|
|
|
|
return ( fReturn);
|
|
} // ILOG_FILE::Close()
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
ILOG_FILE::Flush( VOID)
|
|
/*++
|
|
Flushes the current file.
|
|
The ILOG_FILE object should be locked before calling this function.
|
|
|
|
There should be a valid open log file for flushing.
|
|
Otherwise this function silently returns a TRUE.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is a failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fReturn = TRUE;
|
|
|
|
if ( m_hFile != INVALID_HANDLE_VALUE) {
|
|
|
|
//
|
|
// Write out all the data that is pending to be written
|
|
//
|
|
|
|
if ( m_pvBuffer != NULL && m_cbBufferUsed > 0) {
|
|
|
|
DWORD cbWritten;
|
|
fReturn = WriteFile(m_hFile, m_pvBuffer,
|
|
m_cbBufferUsed,
|
|
&cbWritten, NULL);
|
|
m_cbBufferUsed = 0;
|
|
}
|
|
|
|
IF_DEBUG( INETLOG) {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" FlushFile( %ws. Handle = %08x) returns %d."
|
|
" Error = %d\n",
|
|
m_rgchFileName,
|
|
m_hFile, fReturn,
|
|
( fReturn) ? NO_ERROR: GetLastError()));
|
|
}
|
|
}
|
|
|
|
return ( fReturn);
|
|
} // ILOG_FILE::Flush()
|
|
|
|
|
|
|
|
# if DBG
|
|
VOID
|
|
ILOG_FILE::Print(VOID) const
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" ILOG_FILE(%08x) for %ws\n"
|
|
" File Handle = %08x; References = %d\n"
|
|
" Buffer = %08x; Size = %d; Bytes Used = %d\n"
|
|
,
|
|
this, m_rgchFileName,
|
|
m_hFile, m_cReferences,
|
|
m_pvBuffer, m_cbBufferSize,
|
|
m_cbBufferUsed
|
|
));
|
|
return;
|
|
} // ILOG_FILE::Print()
|
|
|
|
# endif // DBG
|
|
|
|
|
|
|
|
|
|
/************************************************************
|
|
* Static Functions
|
|
************************************************************/
|
|
|
|
|
|
//
|
|
// Class static data initialization
|
|
//
|
|
|
|
LIST_ENTRY ILOG_FILE::sm_listFiles;
|
|
CRITICAL_SECTION ILOG_FILE::sm_csFiles;
|
|
|
|
DWORD ILOG_FILE::sm_nFiles = 0;
|
|
DWORD ILOG_FILE::sm_dwFlushSchedulerCookie = 0;
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
ILOG_FILE::Initialize(VOID)
|
|
{
|
|
InitializeListHead( &ILOG_FILE::sm_listFiles);
|
|
InitializeCriticalSection( &ILOG_FILE::sm_csFiles);
|
|
|
|
DBG_ASSERT( ILOG_FILE::sm_nFiles == 0);
|
|
DBG_ASSERT( ILOG_FILE::sm_dwFlushSchedulerCookie == 0);
|
|
|
|
return ( NO_ERROR);
|
|
|
|
} // ILOG_FILE::Initialize()
|
|
|
|
|
|
DWORD
|
|
ILOG_FILE::Cleanup(VOID)
|
|
{
|
|
//
|
|
// 1. Flush & Close all the files on the list and
|
|
// delete them all
|
|
//
|
|
// Ideally, by the time this function is called,
|
|
// there should be no outstanding open files.
|
|
//
|
|
|
|
DBG_ASSERT( ILOG_FILE::sm_nFiles == 0);
|
|
|
|
if ( ILOG_FILE::sm_nFiles != 0) {
|
|
|
|
PLIST_ENTRY pListEntry;
|
|
PLIST_ENTRY pListEntryNext;
|
|
PILOG_FILE piFileScan;
|
|
|
|
ILOG_FILE::LockFilesList();
|
|
|
|
for(
|
|
pListEntry = ILOG_FILE::sm_listFiles.Flink,
|
|
pListEntryNext = &ILOG_FILE::sm_listFiles;
|
|
|
|
pListEntry != &ILOG_FILE::sm_listFiles;
|
|
|
|
pListEntry = pListEntryNext
|
|
) {
|
|
|
|
piFileScan = CONTAINING_RECORD( pListEntry, ILOG_FILE,
|
|
m_listEntry);
|
|
|
|
DBG_REQUIRE( piFileScan->Close());
|
|
ILOG_FILE::sm_nFiles--;
|
|
|
|
DBG_REQUIRE(piFileScan->Dereference() == 0);
|
|
|
|
pListEntryNext = pListEntry->Flink;
|
|
|
|
delete piFileScan;
|
|
} // for
|
|
|
|
ILOG_FILE::UnlockFilesList();
|
|
}
|
|
|
|
//
|
|
// 2. Remove the scheduler if any pending
|
|
//
|
|
// Again, if everything went fine, the scheduler
|
|
// should have been removed when the last file object
|
|
// was removed by ILOG_FILE::CloseFile()
|
|
//
|
|
|
|
DBG_ASSERT( ILOG_FILE::sm_dwFlushSchedulerCookie == 0);
|
|
|
|
if ( ILOG_FILE::sm_dwFlushSchedulerCookie != 0) {
|
|
|
|
ILOG_FILE::LockFilesList();
|
|
if (RemoveWorkItem( ILOG_FILE::sm_dwFlushSchedulerCookie)) {
|
|
|
|
ILOG_FILE::sm_dwFlushSchedulerCookie = 0;
|
|
}
|
|
ILOG_FILE::UnlockFilesList();
|
|
}
|
|
|
|
|
|
//
|
|
// 3. Destroy the global state
|
|
//
|
|
|
|
DBG_ASSERT( ILOG_FILE::sm_nFiles == 0);
|
|
DBG_ASSERT( ILOG_FILE::sm_dwFlushSchedulerCookie == 0);
|
|
DBG_ASSERT( IsListEmpty( &ILOG_FILE::sm_listFiles));
|
|
DeleteCriticalSection( &sm_csFiles);
|
|
|
|
return ( NO_ERROR);
|
|
|
|
} // ILOG_FILE::Cleanup()
|
|
|
|
|
|
|
|
|
|
PILOG_FILE
|
|
ILOG_FILE::OpenFileForAppend(IN LPCWSTR pszFileName)
|
|
{
|
|
PILOG_FILE piLogFile = NULL;
|
|
PLIST_ENTRY pListEntry;
|
|
PILOG_FILE piFileScan;
|
|
|
|
//
|
|
// 1. Search for this file in the list of files
|
|
// 2. If present, return the ILOG_FILE pointer
|
|
// 3. If absent
|
|
// 4. Create a new ILOG_FILE object for the file
|
|
// 5. Opne the file, set it in append mode and create buffers
|
|
// 6. If this is the first file in list, schedule a flusher
|
|
// 7. Return the new ILOG_FILE object
|
|
//
|
|
|
|
|
|
// 1. Search for this file in the list of files
|
|
|
|
ILOG_FILE::LockFilesList();
|
|
|
|
for( pListEntry = ILOG_FILE::sm_listFiles.Flink;
|
|
pListEntry != &ILOG_FILE::sm_listFiles;
|
|
pListEntry = pListEntry->Flink
|
|
) {
|
|
|
|
piFileScan = CONTAINING_RECORD( pListEntry, ILOG_FILE,
|
|
m_listEntry);
|
|
|
|
if ( !lstrcmpiW( piFileScan->QueryFileName(), pszFileName)) {
|
|
|
|
// a match is found.
|
|
piLogFile = piFileScan;
|
|
piLogFile->Reference(); // increment reference count for object
|
|
}
|
|
|
|
} // for
|
|
|
|
// 2. If present, return the ILOG_FILE pointer
|
|
|
|
if ( piLogFile != NULL) {
|
|
|
|
UnlockFilesList();
|
|
return ( piLogFile);
|
|
}
|
|
|
|
// 3. If absent
|
|
// 4. Create a new ILOG_FILE object for the file
|
|
// 5. Opne the file, set it in append mode and create buffers
|
|
|
|
piLogFile = new ILOG_FILE();
|
|
|
|
if ( piLogFile != NULL) {
|
|
|
|
piLogFile->Lock();
|
|
|
|
if ( !piLogFile->Open( pszFileName)) {
|
|
|
|
// unable to open the log file
|
|
// return appropriate error
|
|
|
|
piLogFile->Close(); // to cleanup any temporary state
|
|
|
|
piLogFile->Unlock();
|
|
|
|
delete piLogFile;
|
|
piLogFile = NULL;
|
|
|
|
} else {
|
|
|
|
piLogFile->Reference();
|
|
InsertTailList( &sm_listFiles, &piLogFile->m_listEntry);
|
|
piLogFile->Unlock();
|
|
ILOG_FILE::sm_nFiles++;
|
|
}
|
|
}
|
|
|
|
// 6. If this is the first file in list, schedule a flusher
|
|
|
|
if ( ILOG_FILE::sm_nFiles == 1 && piLogFile != NULL) {
|
|
|
|
//
|
|
// This is the first log file that is created.
|
|
// Schedule a flusher for the files
|
|
//
|
|
|
|
DBG_REQUIRE( ILOG_FILE::ScheduleFlush());
|
|
}
|
|
|
|
ILOG_FILE::UnlockFilesList();
|
|
|
|
return ( piLogFile);
|
|
|
|
} // ILOG_FILE::OpenFileForAppend()
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
ILOG_FILE::CloseFile( IN ILOG_FILE ** ppLogFile)
|
|
/*++
|
|
This function closes the file object specified in *ppLogFile
|
|
|
|
Arguments:
|
|
ppLogFile pointer to pointer to log file.
|
|
if cleanup is successful, then it stores NULL
|
|
in *ppLogFile
|
|
Returns:
|
|
NO_ERROR on success and error on failure
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
PILOG_FILE pLogFile;
|
|
|
|
DBG_ASSERT( ppLogFile != NULL);
|
|
|
|
// 1. If valid file object, do cleanup
|
|
|
|
pLogFile = *ppLogFile;
|
|
|
|
if ( pLogFile != NULL) {
|
|
|
|
// 2. Flush out the log buffers
|
|
|
|
pLogFile->Lock();
|
|
DBG_REQUIRE( pLogFile->Flush());
|
|
pLogFile->Unlock();
|
|
|
|
|
|
// 3. prepare for cleanup, deref the object.
|
|
// If ref drops to 0, then close and destroy file object
|
|
if ( pLogFile->Dereference() == 0) {
|
|
|
|
pLogFile->Lock();
|
|
DBG_REQUIRE( pLogFile->Close());
|
|
pLogFile->Unlock();
|
|
|
|
ILOG_FILE::LockFilesList();
|
|
|
|
RemoveEntryList( &pLogFile->m_listEntry);
|
|
|
|
DBG_CODE( InitializeListHead( &pLogFile->m_listEntry));
|
|
|
|
ILOG_FILE::sm_nFiles--;
|
|
|
|
// 4. Remove scheduler if no more files remain open
|
|
|
|
if ( ILOG_FILE::sm_nFiles == 0 &&
|
|
ILOG_FILE::sm_dwFlushSchedulerCookie != 0) {
|
|
|
|
if (RemoveWorkItem( ILOG_FILE::sm_dwFlushSchedulerCookie)) {
|
|
|
|
ILOG_FILE::sm_dwFlushSchedulerCookie = 0;
|
|
}
|
|
}
|
|
|
|
ILOG_FILE::UnlockFilesList();
|
|
|
|
//
|
|
// 5. Destroy the object
|
|
//
|
|
|
|
delete pLogFile;
|
|
}
|
|
}
|
|
|
|
// 6. Set state of cleanup in the log object
|
|
|
|
*ppLogFile = NULL;
|
|
|
|
return ( dwError);
|
|
|
|
} // ILOG_FILE::CloseFile()
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
ILOG_FILE::ScheduleFlush(VOID)
|
|
/*++
|
|
|
|
This function schedules a flusher for files objects.
|
|
This should be called after holding the ILOG_FILE::LockFilesList()
|
|
|
|
If a flush has already been scheduled,
|
|
then this function silently returns 0.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if there is a problem in starting the scheduler
|
|
|
|
--*/
|
|
{
|
|
BOOL fReturn = TRUE;
|
|
|
|
if (sm_dwFlushSchedulerCookie == 0 &&
|
|
g_cmsecIlogFileFlushInterval != INFINITE
|
|
) {
|
|
|
|
sm_dwFlushSchedulerCookie =
|
|
ScheduleWorkItem((PFN_SCHED_CALLBACK ) LogFileFlushHandler,
|
|
NULL,
|
|
g_cmsecIlogFileFlushInterval);
|
|
|
|
fReturn = (sm_dwFlushSchedulerCookie != 0);
|
|
|
|
if ( !fReturn) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[FileLog] ScheduleWorkItem failed, error %d, "
|
|
" File Flush disabled\n",
|
|
GetLastError() ));
|
|
}
|
|
}
|
|
|
|
return ( fReturn);
|
|
|
|
} // ILOG_FILE::ScheduleFlush()
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
ILOG_FILE::FlushHandler(VOID)
|
|
/*++
|
|
This class static function handles flushing for file objects open.
|
|
This handles callbacks from the scheduler and flushes
|
|
the buffers.
|
|
|
|
After successfully handling the flushes,
|
|
if the number of open files is non-zero it schedules
|
|
another flush operation.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
TRUE on success
|
|
FALSE if there is any problem in scheduling a flush.
|
|
|
|
--*/
|
|
{
|
|
BOOL fReturn = TRUE;
|
|
PLIST_ENTRY pListEntry;
|
|
PILOG_FILE piFileScan;
|
|
|
|
ILOG_FILE::LockFilesList();
|
|
|
|
DBG_ASSERT( ILOG_FILE::sm_dwFlushSchedulerCookie != 0);
|
|
|
|
ILOG_FILE::sm_dwFlushSchedulerCookie = 0;
|
|
|
|
|
|
// 1. Flush all open files
|
|
|
|
for( pListEntry = sm_listFiles.Flink;
|
|
pListEntry != &sm_listFiles;
|
|
pListEntry = pListEntry->Flink
|
|
) {
|
|
|
|
piFileScan = CONTAINING_RECORD( pListEntry, ILOG_FILE,
|
|
m_listEntry);
|
|
|
|
piFileScan->Reference();
|
|
|
|
fReturn = (fReturn && piFileScan->Flush());
|
|
|
|
piFileScan->Dereference();
|
|
} // for
|
|
|
|
|
|
//
|
|
// 2. Reschedule for the next interval if need be
|
|
//
|
|
|
|
if ( fReturn && sm_nFiles >= 1) {
|
|
|
|
fReturn = ScheduleFlush();
|
|
}
|
|
|
|
ILOG_FILE::UnlockFilesList();
|
|
|
|
return (fReturn);
|
|
} // ILOG_FILE::FlushHandler()
|
|
|
|
|
|
|
|
|
|
/**************************************************
|
|
* Auxiliary Functions
|
|
**************************************************/
|
|
|
|
|
|
|
|
static VOID
|
|
LogFileFlushHandler(VOID * pContext)
|
|
/*++
|
|
C wrapper function for the C++ Flush Handler
|
|
--*/
|
|
{
|
|
DBG_REQUIRE( ILOG_FILE::FlushHandler());
|
|
|
|
return;
|
|
} // LogFileFlushHandler()
|
|
|
|
/************************ End of File ***********************/
|