Adam Johnson
13 years ago
27 changed files with 151 additions and 2838 deletions
File diff suppressed because it is too large
Load Diff
@ -1,518 +0,0 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools |
||||
Copyright (C) 2011 Cyan Worlds, Inc. |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7 |
||||
|
||||
If you modify this Program, or any covered work, by linking or |
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
||||
(or a modified version of those libraries), |
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
||||
licensors of this Program grant you additional |
||||
permission to convey the resulting work. Corresponding Source for a |
||||
non-source form of such a combination shall include the source code for |
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
||||
work. |
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
||||
or by snail mail at: |
||||
Cyan Worlds, Inc. |
||||
14617 N Newport Hwy |
||||
Mead, WA 99021 |
||||
|
||||
*==LICENSE==*/ |
||||
/*****************************************************************************
|
||||
* |
||||
* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xFile.cpp |
||||
*
|
||||
***/ |
||||
|
||||
#include "../../Pch.h" |
||||
#pragma hdrstop |
||||
|
||||
#include "pnAceW9xInt.h" |
||||
|
||||
|
||||
namespace W9x { |
||||
|
||||
|
||||
/****************************************************************************
|
||||
* |
||||
* FileOp |
||||
* |
||||
***/ |
||||
|
||||
struct FileOp { |
||||
EAsyncNotifyFile code; |
||||
bool notify; |
||||
union { |
||||
AsyncNotifyFileFlush flush; |
||||
AsyncNotifyFileRead read; |
||||
AsyncNotifyFileSequence sequence; |
||||
AsyncNotifyFileWrite write; |
||||
} data; |
||||
}; |
||||
|
||||
|
||||
/****************************************************************************
|
||||
* |
||||
* CFile |
||||
* |
||||
***/ |
||||
|
||||
class CFile : public CThreadDispObject { |
||||
private: |
||||
CCritSect m_critSect; |
||||
HANDLE m_handle; |
||||
FAsyncNotifyFileProc m_notifyProc; |
||||
void * m_userState; |
||||
|
||||
protected: |
||||
void Complete (void * op, CCritSect * critSect, AsyncId asyncId); |
||||
void Delete (void * op); |
||||
|
||||
public: |
||||
CFile ( |
||||
HANDLE handle, |
||||
FAsyncNotifyFileProc notifyProc, |
||||
void * userState |
||||
); |
||||
~CFile (); |
||||
|
||||
void Read ( |
||||
uint64_t offset, |
||||
void * buffer, |
||||
unsigned bytes |
||||
); |
||||
|
||||
void SetLastWriteTime (uint64_t lastWriteTime); |
||||
|
||||
void Truncate (uint64_t size); |
||||
|
||||
void Write ( |
||||
uint64_t offset, |
||||
const void * buffer, |
||||
unsigned bytes |
||||
); |
||||
|
||||
bool Seek (uint64_t offset, EFileSeekFrom from); |
||||
}; |
||||
|
||||
//===========================================================================
|
||||
CFile::CFile ( |
||||
HANDLE handle, |
||||
FAsyncNotifyFileProc notifyProc, |
||||
void * userState |
||||
) : |
||||
m_handle(handle), |
||||
m_notifyProc(notifyProc), |
||||
m_userState(userState) |
||||
{ |
||||
} |
||||
|
||||
//===========================================================================
|
||||
CFile::~CFile () { |
||||
CloseHandle(m_handle); |
||||
m_handle = INVALID_HANDLE_VALUE; |
||||
} |
||||
|
||||
//===========================================================================
|
||||
void CFile::Complete (void * op, CCritSect * critSect, AsyncId asyncId) { |
||||
FileOp * fileOp = (FileOp *)op; |
||||
|
||||
// Enter our local critical section and leave the global one
|
||||
m_critSect.Enter(); |
||||
critSect->Leave(); |
||||
|
||||
// Complete the operation
|
||||
switch (fileOp->code) { |
||||
|
||||
case kNotifyFileFlush: { |
||||
if (fileOp->data.flush.truncateSize != kAsyncFileDontTruncate) |
||||
Truncate(fileOp->data.flush.truncateSize); |
||||
BOOL result = FlushFileBuffers(m_handle); |
||||
fileOp->data.flush.error = result ? kFileSuccess : AsyncGetLastFileError(); |
||||
} |
||||
break; |
||||
|
||||
case kNotifyFileRead: |
||||
Read( |
||||
fileOp->data.read.offset, |
||||
fileOp->data.read.buffer, |
||||
fileOp->data.read.bytes |
||||
); |
||||
break; |
||||
|
||||
case kNotifyFileWrite: |
||||
Write( |
||||
fileOp->data.write.offset, |
||||
fileOp->data.write.buffer, |
||||
fileOp->data.write.bytes |
||||
); |
||||
break; |
||||
|
||||
} |
||||
|
||||
// Leave our local critical section
|
||||
m_critSect.Leave(); |
||||
|
||||
// Dispatch a completion notification
|
||||
if (fileOp->notify) { |
||||
fileOp->data.flush.asyncId = asyncId; |
||||
m_notifyProc( |
||||
(AsyncFile)this,
|
||||
fileOp->code, |
||||
&fileOp->data.flush, |
||||
&m_userState |
||||
); |
||||
} |
||||
|
||||
} |
||||
|
||||
//===========================================================================
|
||||
void CFile::Delete (void * op) { |
||||
FileOp * fileOp = (FileOp *)op; |
||||
delete fileOp; |
||||
} |
||||
|
||||
//===========================================================================
|
||||
void CFile::Read ( |
||||
uint64_t offset, |
||||
void * buffer, |
||||
unsigned bytes |
||||
) { |
||||
|
||||
// Seek to the start of the read
|
||||
Seek(offset, kFileSeekFromBegin); |
||||
|
||||
// Perform the read
|
||||
DWORD bytesRead; |
||||
BOOL result = ReadFile( |
||||
m_handle, |
||||
buffer, |
||||
bytes, |
||||
&bytesRead, |
||||
nil // overlapped
|
||||
); |
||||
|
||||
// Handle errors
|
||||
if (bytesRead != bytes) |
||||
memset((uint8_t *)buffer + bytesRead, 0, bytes - bytesRead); |
||||
if ( (!result && (GetLastError() != ERROR_IO_PENDING)) || |
||||
(bytesRead != bytes) ) |
||||
LogMsg(kLogFatal, "failed: ReadFile"); |
||||
|
||||
} |
||||
|
||||
//===========================================================================
|
||||
bool CFile::Seek (uint64_t offset, EFileSeekFrom from) { |
||||
COMPILER_ASSERT(kFileSeekFromBegin == FILE_BEGIN); |
||||
COMPILER_ASSERT(kFileSeekFromCurrent == FILE_CURRENT); |
||||
COMPILER_ASSERT(kFileSeekFromEnd == FILE_END); |
||||
|
||||
LONG low = (LONG)(offset % 0x100000000ul); |
||||
LONG high = (LONG)(offset / 0x100000000ul); |
||||
uint32_t result = SetFilePointer(m_handle, low, &high, from); |
||||
if ((result == (uint32_t)-1) && (GetLastError() != NO_ERROR)) { |
||||
LogMsg(kLogFatal, "failed: SetFilePointer"); |
||||
return false; |
||||
} |
||||
else |
||||
return true; |
||||
} |
||||
|
||||
//===========================================================================
|
||||
void CFile::SetLastWriteTime (uint64_t lastWriteTime) { |
||||
COMPILER_ASSERT(sizeof(lastWriteTime) == sizeof(FILETIME)); |
||||
SetFileTime(m_handle, nil, nil, (const FILETIME *)&lastWriteTime); |
||||
} |
||||
|
||||
//===========================================================================
|
||||
void CFile::Truncate (uint64_t size) { |
||||
ASSERT(size != kAsyncFileDontTruncate); |
||||
|
||||
if (Seek(size, kFileSeekFromBegin) && !SetEndOfFile(m_handle))
|
||||
LogMsg(kLogFatal, "failed: SetEndOfFile"); |
||||
} |
||||
|
||||
//===========================================================================
|
||||
void CFile::Write ( |
||||
uint64_t offset, |
||||
const void * buffer, |
||||
unsigned bytes |
||||
) { |
||||
|
||||
// Seek to the start of the write
|
||||
Seek(offset, kFileSeekFromBegin); |
||||
|
||||
// Perform the write
|
||||
DWORD bytesWritten; |
||||
BOOL result = WriteFile( |
||||
m_handle, |
||||
buffer, |
||||
bytes, |
||||
&bytesWritten, |
||||
nil // overlapped
|
||||
); |
||||
|
||||
// Handle errors
|
||||
if ( (!result && (GetLastError() != ERROR_IO_PENDING)) || |
||||
(bytesWritten != bytes) ) { |
||||
LogMsg(kLogFatal, "failed: WriteFile"); |
||||
if (!result && (GetLastError() == ERROR_DISK_FULL)) { |
||||
MessageBox(nil, "Disk full!", "Error", MB_ICONSTOP | MB_SYSTEMMODAL); |
||||
// DebugDisableLeakChecking();
|
||||
ExitProcess(1); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
/****************************************************************************
|
||||
* |
||||
* Exported functions |
||||
* |
||||
***/ |
||||
|
||||
//===========================================================================
|
||||
void W9xFileClose ( |
||||
AsyncFile file,
|
||||
uint64_t truncateSize |
||||
) { |
||||
|
||||
// Dereference the object
|
||||
CFile * object = (CFile *)file; |
||||
|
||||
// If requested, truncate the file
|
||||
if (truncateSize != kAsyncFileDontTruncate) |
||||
object->Truncate(truncateSize);
|
||||
|
||||
// Close the file object
|
||||
object->Close(); |
||||
|
||||
} |
||||
|
||||
//===========================================================================
|
||||
AsyncId W9xFileCreateSequence ( |
||||
AsyncFile file,
|
||||
bool notify,
|
||||
void * param |
||||
) { |
||||
|
||||
// Dereference the object
|
||||
CFile * object = (CFile *)file; |
||||
|
||||
// Queue an operation
|
||||
FileOp * op = new FileOp; |
||||
op->code = kNotifyFileSequence; |
||||
op->notify = notify; |
||||
op->data.flush.param = param; |
||||
return object->Queue(op); |
||||
|
||||
} |
||||
|
||||
//===========================================================================
|
||||
AsyncId W9xFileFlushBuffers ( |
||||
AsyncFile file,
|
||||
uint64_t truncateSize, |
||||
bool notify, |
||||
void * param |
||||
) { |
||||
|
||||
// Dereference the object
|
||||
CFile * object = (CFile *)file; |
||||
|
||||
// Queue an operation
|
||||
FileOp * op = new FileOp; |
||||
op->code = kNotifyFileFlush; |
||||
op->notify = notify; |
||||
op->data.flush.param = param; |
||||
op->data.flush.truncateSize = truncateSize; |
||||
// op->data.flush.error filled in upon completion
|
||||
return object->Queue(op); |
||||
|
||||
} |
||||
|
||||
//===========================================================================
|
||||
AsyncFile W9xFileOpen ( |
||||
const wchar_t fullPath[], |
||||
FAsyncNotifyFileProc notifyProc, |
||||
EFileError * error, |
||||
unsigned desiredAccess, |
||||
unsigned openMode, |
||||
unsigned shareModeFlags, |
||||
void * userState, |
||||
uint64_t * fileSize, |
||||
uint64_t * fileLastWriteTime |
||||
) { |
||||
HANDLE fileHandle = CreateFileW( |
||||
fullPath, |
||||
desiredAccess, |
||||
shareModeFlags, |
||||
nil, // plSecurityAttributes
|
||||
openMode, |
||||
0, // attributeFlags
|
||||
nil // hTemplateFile
|
||||
); |
||||
*error = AsyncGetLastFileError(); |
||||
|
||||
if (INVALID_HANDLE_VALUE == fileHandle) |
||||
return nil; |
||||
|
||||
// don't allow users to open devices like "LPT1", etc.
|
||||
if (GetFileType(fileHandle) != FILE_TYPE_DISK) { |
||||
LogMsg(kLogFatal, "failed: !FILE_TYPE_DISK"); |
||||
*error = kFileErrorFileNotFound; |
||||
CloseHandle(fileHandle); |
||||
return nil; |
||||
} |
||||
|
||||
// Get the file size
|
||||
DWORD sizeHi, sizeLo = GetFileSize(fileHandle, &sizeHi); |
||||
if ((sizeLo == (DWORD) -1) && (NO_ERROR != GetLastError())) { |
||||
*error = AsyncGetLastFileError(); |
||||
LogMsg(kLogFatal, "failed: GetFileSize"); |
||||
CloseHandle(fileHandle); |
||||
return nil; |
||||
} |
||||
const uint64_t size = ((uint64_t) sizeHi << (uint64_t) 32) | (uint64_t) sizeLo; |
||||
|
||||
uint64_t lastWriteTime; |
||||
ASSERT(sizeof(lastWriteTime) >= sizeof(FILETIME)); |
||||
GetFileTime(fileHandle, nil, nil, (FILETIME *) &lastWriteTime); |
||||
|
||||
// Create a file object
|
||||
CFile * object = new CFile( |
||||
fileHandle, |
||||
notifyProc, |
||||
userState |
||||
); |
||||
|
||||
// return out parameters
|
||||
if (fileSize) |
||||
*fileSize = size; |
||||
if (fileLastWriteTime) |
||||
*fileLastWriteTime = lastWriteTime; |
||||
return (AsyncFile)object; |
||||
} |
||||
|
||||
//===========================================================================
|
||||
AsyncId W9xFileRead ( |
||||
AsyncFile file, |
||||
uint64_t offset, |
||||
void * buffer, |
||||
unsigned bytes, |
||||
unsigned flags, |
||||
void * param |
||||
) { |
||||
|
||||
// Dereference the object
|
||||
CFile * object = (CFile *)file; |
||||
|
||||
// Perform synchronous operations immediately
|
||||
if (flags & kAsyncFileRwSync) { |
||||
object->Read(offset, buffer, bytes); |
||||
return 0; |
||||
} |
||||
|
||||
// Queue asynchronous operations
|
||||
else { |
||||
FileOp * op = new FileOp; |
||||
op->code = kNotifyFileRead; |
||||
op->notify = (flags & kAsyncFileRwNotify) != 0; |
||||
op->data.read.param = param; |
||||
op->data.read.offset = offset; |
||||
op->data.read.buffer = (uint8_t *)buffer; |
||||
op->data.read.bytes = bytes; |
||||
return object->Queue(op); |
||||
} |
||||
|
||||
} |
||||
|
||||
//===========================================================================
|
||||
void W9xFileSetLastWriteTime ( |
||||
AsyncFile file,
|
||||
uint64_t lastWriteTime |
||||
) { |
||||
|
||||
// Dereference the object
|
||||
CFile * object = (CFile *)file; |
||||
|
||||
// Set the file time
|
||||
object->SetLastWriteTime(lastWriteTime); |
||||
|
||||
} |
||||
|
||||
//===========================================================================
|
||||
uint64_t W9xFileGetLastWriteTime ( |
||||
const wchar_t fileName[] |
||||
) { |
||||
WIN32_FILE_ATTRIBUTE_DATA info; |
||||
bool f = GetFileAttributesExW(fileName, GetFileExInfoStandard, &info); |
||||
return f ? *((uint64_t *) &info.ftLastWriteTime) : 0; |
||||
} |
||||
|
||||
//===========================================================================
|
||||
AsyncId W9xFileWrite ( |
||||
AsyncFile file, |
||||
uint64_t offset, |
||||
const void * buffer, |
||||
unsigned bytes, |
||||
unsigned flags, |
||||
void * param |
||||
) { |
||||
|
||||
// Dereference the object
|
||||
CFile * object = (CFile *)file; |
||||
|
||||
// Perform synchronous operations immediately
|
||||
if (flags & kAsyncFileRwSync) { |
||||
object->Write(offset, buffer, bytes); |
||||
return 0; |
||||
} |
||||
|
||||
// Queue asynchronous operations
|
||||
else { |
||||
FileOp * op = new FileOp; |
||||
op->code = kNotifyFileWrite; |
||||
op->notify = (flags & kAsyncFileRwNotify) != 0; |
||||
op->data.write.param = param; |
||||
op->data.write.offset = offset; |
||||
op->data.write.buffer = (uint8_t *)buffer; |
||||
op->data.write.bytes = bytes; |
||||
return object->Queue(op); |
||||
} |
||||
|
||||
} |
||||
|
||||
//============================================================================
|
||||
bool W9xFileSeek ( |
||||
AsyncFile file, |
||||
uint64_t distance, |
||||
EFileSeekFrom from |
||||
) { |
||||
CFile * object = (CFile *)file; |
||||
return object->Seek(distance, from); |
||||
} |
||||
|
||||
|
||||
} // namespace W9x
|
@ -1,478 +0,0 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools |
||||
Copyright (C) 2011 Cyan Worlds, Inc. |
||||
|
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7 |
||||
|
||||
If you modify this Program, or any covered work, by linking or |
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
||||
(or a modified version of those libraries), |
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
||||
licensors of this Program grant you additional |
||||
permission to convey the resulting work. Corresponding Source for a |
||||
non-source form of such a combination shall include the source code for |
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
||||
work. |
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
||||
or by snail mail at: |
||||
Cyan Worlds, Inc. |
||||
14617 N Newport Hwy |
||||
Mead, WA 99021 |
||||
|
||||
*==LICENSE==*/ |
||||
/*****************************************************************************
|
||||
* |
||||
* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceLog.cpp |
||||
*
|
||||
***/ |
||||
|
||||
#include "Pch.h" |
||||
#pragma hdrstop |
||||
|
||||
#if defined(PLASMA_EXTERNAL_RELEASE) |
||||
// If this is an external build then don't write log files
|
||||
#define ACELOG_NO_LOG_FILES |
||||
#endif |
||||
|
||||
|
||||
namespace AsyncLog { |
||||
|
||||
/****************************************************************************
|
||||
* |
||||
* Private |
||||
* |
||||
***/ |
||||
|
||||
static const unsigned kLogFlushMs = 10 * 1000; |
||||
|
||||
enum ELogType { |
||||
#ifdef SERVER |
||||
kLogTypeDebug, |
||||
kLogTypePerf, |
||||
kLogTypeError, |
||||
#else |
||||
kLogTypeDebug, |
||||
#endif |
||||
kNumLogTypes |
||||
}; |
||||
|
||||
static bool s_breakOnErrors; |
||||
static wchar_t s_directory[MAX_PATH]; |
||||
static CCritSect s_logCrit[kNumLogTypes]; |
||||
static char * s_logBuf[kNumLogTypes]; |
||||
static unsigned s_logPos[kNumLogTypes]; |
||||
static uint64_t s_logWritePos[kNumLogTypes]; |
||||
static TimeDesc s_logTime[kNumLogTypes]; |
||||
static unsigned s_logWriteMs[kNumLogTypes]; |
||||
static AsyncFile s_logFile[kNumLogTypes]; |
||||
static long s_opsPending; |
||||
static bool s_running; |
||||
static AsyncTimer * s_timer; |
||||
|
||||
static unsigned s_logSize[kNumLogTypes] = { |
||||
#ifdef SERVER |
||||
64 * 1024, |
||||
64 * 1024, |
||||
8 * 1024, |
||||
#else |
||||
64 * 1024, |
||||
#endif |
||||
}; |
||||
|
||||
static const wchar_t * s_logNameFmt[kNumLogTypes] = { |
||||
#ifdef SERVER |
||||
L"Dbg%02u%02u%02u.log", |
||||
L"Inf%02u%02u%02u.log", |
||||
L"Err%02u%02u%02u.log", |
||||
#else |
||||
L"%s%02u%02u%02u.log", |
||||
#endif |
||||
}; |
||||
|
||||
static ELogType s_logSeverityToType[kNumLogSeverity] = { |
||||
#ifdef SERVER |
||||
kLogTypeDebug, // kLogDebug
|
||||
kLogTypePerf, // kLogPerf
|
||||
kLogTypeError, // kLogError
|
||||
kLogTypeError, // kLogFatal
|
||||
#else |
||||
kLogTypeDebug, // kLogDebug
|
||||
kLogTypeDebug, // kLogPerf
|
||||
kLogTypeDebug, // kLogError
|
||||
kLogTypeDebug, // kLogFatal
|
||||
#endif |
||||
}; |
||||
|
||||
static char * s_logSeverityToText[kNumLogSeverity] = { |
||||
"Debug", |
||||
"Info", |
||||
"Error", |
||||
"Fatal", |
||||
}; |
||||
|
||||
|
||||
/****************************************************************************
|
||||
* |
||||
* Local functions |
||||
* |
||||
***/ |
||||
|
||||
//============================================================================
|
||||
static void LogFileNotifyProc ( |
||||
AsyncFile file, |
||||
EAsyncNotifyFile code, |
||||
AsyncNotifyFile * notify, |
||||
void ** userState |
||||
) { |
||||
switch (code) { |
||||
case kNotifyFileWrite: |
||||
free(notify->param); |
||||
AtomicAdd(&s_opsPending, -1); |
||||
break; |
||||
|
||||
case kNotifyFileFlush: |
||||
AsyncFileClose(file, kAsyncFileDontTruncate); |
||||
AtomicAdd(&s_opsPending, -1); |
||||
break; |
||||
|
||||
DEFAULT_FATAL(code); |
||||
} |
||||
} |
||||
|
||||
//============================================================================
|
||||
static void AllocLogBuffer_CS (unsigned index) { |
||||
ASSERT(!s_logBuf[index]); |
||||
s_logBuf[index] = (char *)malloc(s_logSize[index]); |
||||
s_logPos[index] = 0; |
||||
|
||||
if (!s_logBuf[index]) |
||||
ErrorAssert(__LINE__, __FILE__, "Out of memory"); |
||||
} |
||||
|
||||
//============================================================================
|
||||
static void FreeLogBuffer_CS (unsigned index) { |
||||
if (s_logBuf[index]) { |
||||
free(s_logBuf[index]); |
||||
s_logBuf[index] = nil; |
||||
} |
||||
} |
||||
|
||||
//============================================================================
|
||||
static void GetLogFilename ( |
||||
unsigned index, |
||||
TimeDesc timeDesc, |
||||
wchar_t * filename, |
||||
unsigned chars |
||||
) { |
||||
StrPrintf( |
||||
filename, |
||||
chars, |
||||
s_logNameFmt[index], |
||||
#ifndef SERVER |
||||
ProductShortName(), |
||||
#endif |
||||
timeDesc.year % 100, |
||||
timeDesc.month, |
||||
timeDesc.day |
||||
); |
||||
PathAddFilename(filename, s_directory, filename, chars); |
||||
} |
||||
|
||||
//============================================================================
|
||||
static bool OpenLogFile_CS (unsigned index) { |
||||
if (s_logFile[index] != nil) |
||||
return true; |
||||
|
||||
// Build filename
|
||||
wchar_t filename[MAX_PATH]; |
||||
GetLogFilename( |
||||
index, |
||||
s_logTime[index], |
||||
filename, |
||||
arrsize(filename) |
||||
); |
||||
|
||||
// Open file
|
||||
uint64_t fileTime; |
||||
EFileError fileError; |
||||
bool fileExist = PathDoesFileExist(filename); |
||||
s_logFile[index] = AsyncFileOpen( |
||||
filename, |
||||
LogFileNotifyProc, |
||||
&fileError, |
||||
kAsyncFileWriteAccess, |
||||
kAsyncFileModeOpenAlways, |
||||
kAsyncFileShareRead, |
||||
nil, // userState
|
||||
&s_logWritePos[index], |
||||
&fileTime |
||||
); |
||||
|
||||
if (s_logFile[index] == nil) |
||||
return false; |
||||
|
||||
TimeGetDesc(fileTime, &s_logTime[index]); |
||||
s_logWriteMs[index] = TimeGetMs(); |
||||
|
||||
// Seek to end of file
|
||||
AsyncFileSeek(s_logFile[index], s_logWritePos[index], kFileSeekFromBegin); |
||||
|
||||
// If this is a new file, write uint8_t Order Mark
|
||||
if (!fileExist) { |
||||
static const char s_bom[] = "\xEF\xBB\xBF"; |
||||
AsyncFileWrite( |
||||
s_logFile[index], |
||||
s_logWritePos[index], |
||||
s_bom, |
||||
arrsize(s_bom)- 1, |
||||
kAsyncFileRwSync, // perform blocking write
|
||||
nil // param
|
||||
); |
||||
s_logWritePos[index] += arrsize(s_bom) - 1; |
||||
} |
||||
|
||||
// Write a sentinel in case there are multiple runs in one day
|
||||
static const char s_logOpened[] = "Log Opened\r\n"; |
||||
AsyncFileWrite( |
||||
s_logFile[index], |
||||
s_logWritePos[index], |
||||
s_logOpened, |
||||
arrsize(s_logOpened)- 1, |
||||
kAsyncFileRwSync, // perform blocking write
|
||||
nil |
||||
); |
||||
s_logWritePos[index] += arrsize(s_logOpened) - 1; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
//============================================================================
|
||||
static void WriteLogFile_CS (unsigned index, bool close) { |
||||
unsigned flags = kAsyncFileRwSync; // kAsyncFileRwNotify
|
||||
if (s_logPos[index]) { |
||||
if (OpenLogFile_CS(index)) { |
||||
AsyncFileWrite( |
||||
s_logFile[index], |
||||
s_logWritePos[index], |
||||
s_logBuf[index], |
||||
s_logPos[index], |
||||
flags, |
||||
s_logBuf[index] |
||||
); |
||||
if (flags == kAsyncFileRwSync) |
||||
delete s_logBuf[index]; |
||||
else |
||||
AtomicAdd(&s_opsPending, 1); |
||||
s_logWritePos[index] += s_logPos[index]; |
||||
s_logWriteMs[index] = TimeGetMs(); |
||||
s_logBuf[index] = nil; |
||||
s_logPos[index] = 0; |
||||
} |
||||
} |
||||
|
||||
if (close && s_logFile[index]) { |
||||
if (flags == kAsyncFileRwNotify) { |
||||
AtomicAdd(&s_opsPending, 1); |
||||
AsyncFileFlushBuffers( |
||||
s_logFile[index], |
||||
kAsyncFileDontTruncate, |
||||
true, |
||||
nil |
||||
); |
||||
} |
||||
else { |
||||
AsyncFileClose( |
||||
s_logFile[index], |
||||
kAsyncFileDontTruncate |
||||
); |
||||
} |
||||
s_logFile[index] = nil; |
||||
} |
||||
} |
||||
|
||||
//============================================================================
|
||||
static void FlushLogFile_CS ( |
||||
unsigned index, |
||||
TimeDesc timeDesc |
||||
) { |
||||
bool close = !s_running || (s_logTime[index].day != timeDesc.day); |
||||
WriteLogFile_CS(index, close); |
||||
if (close) |
||||
s_logTime[index] = timeDesc; |
||||
} |
||||
|
||||
//============================================================================
|
||||
static unsigned FlushLogsTimerCallback (void *) { |
||||
AsyncLogFlush(); |
||||
return kAsyncTimeInfinite; |
||||
} |
||||
|
||||
|
||||
} using namespace AsyncLog; |
||||
|
||||
|
||||
/****************************************************************************
|
||||
* |
||||
* Exported functions |
||||
* |
||||
***/ |
||||
|
||||
//============================================================================
|
||||
void AsyncLogInitialize ( |
||||
const wchar_t logDirName[], |
||||
bool breakOnErrors |
||||
) { |
||||
s_running = true; |
||||
|
||||
// Save options
|
||||
s_breakOnErrors = breakOnErrors; |
||||
|
||||
// Build log directory name
|
||||
#ifdef SERVER |
||||
PathGetProgramDirectory(s_directory, arrsize(s_directory)); |
||||
#else |
||||
PathGetUserDirectory(s_directory, arrsize(s_directory)); |
||||
#endif |
||||
PathAddFilename(s_directory, s_directory, logDirName, arrsize(s_directory)); |
||||
|
||||
#ifndef ACELOG_NO_LOG_FILES |
||||
// Create log directory
|
||||
if (kPathCreateDirSuccess != PathCreateDirectory(s_directory, 0)) |
||||
PathRemoveFilename(s_directory, s_directory, arrsize(s_directory)); |
||||
|
||||
// Allocate log buffers
|
||||
for (unsigned index = 0; index < kNumLogTypes; ++index) { |
||||
s_logCrit[index].Enter(); |
||||
AllocLogBuffer_CS(index); |
||||
s_logCrit[index].Leave(); |
||||
} |
||||
|
||||
AsyncTimerCreate(&s_timer, FlushLogsTimerCallback, kAsyncTimeInfinite, nil); |
||||
#endif // ndef ACELOG_NO_LOG_FILES
|
||||
} |
||||
|
||||
//============================================================================
|
||||
void AsyncLogDestroy () { |
||||
s_running = false; |
||||
|
||||
#ifndef ACELOG_NO_LOG_FILES |
||||
AsyncTimerDelete(s_timer, kAsyncTimerDestroyWaitComplete); |
||||
|
||||
for (unsigned index = 0; index < kNumLogTypes; ++index) { |
||||
s_logCrit[index].Enter(); |
||||
{ |
||||
WriteLogFile_CS(index, true); |
||||
FreeLogBuffer_CS(index); |
||||
} |
||||
s_logCrit[index].Leave(); |
||||
} |
||||
while (s_opsPending) |
||||
AsyncSleep(10); |
||||
#endif // ndef ACELOG_NO_LOG_FILES
|
||||
} |
||||
|
||||
//============================================================================
|
||||
void AsyncLogFlush () { |
||||
#ifndef ACELOG_NO_LOG_FILES |
||||
TimeDesc timeDesc; |
||||
TimeGetDesc(TimeGetTime(), &timeDesc); |
||||
|
||||
for (unsigned index = 0; index < kNumLogTypes; ++index) { |
||||
s_logCrit[index].Enter(); |
||||
FlushLogFile_CS(index, timeDesc); |
||||
s_logCrit[index].Leave(); |
||||
} |
||||
#endif // ndef ACELOG_NO_LOG_FILES
|
||||
} |
||||
|
||||
//============================================================================
|
||||
void LogBreakOnErrors (bool breakOnErrors) { |
||||
s_breakOnErrors = breakOnErrors; |
||||
} |
||||
|
||||
//============================================================================
|
||||
void AsyncLogWriteMsg ( |
||||
const wchar_t facility[], |
||||
ELogSeverity severity, |
||||
const wchar_t msg[] |
||||
) { |
||||
if (!s_running) |
||||
return; |
||||
|
||||
#ifndef ACELOG_NO_LOG_FILES |
||||
TimeDesc timeDesc; |
||||
TimeGetDesc(TimeGetTime(), &timeDesc); |
||||
|
||||
char buffer[2048]; |
||||
const unsigned chars = StrPrintf( |
||||
buffer, |
||||
arrsize(buffer), |
||||
"%02u/%02u/%02u % 2u:%02u:%02u [%S] %s %S\r\n", |
||||
timeDesc.month, |
||||
timeDesc.day, |
||||
timeDesc.year % 100, |
||||
timeDesc.hour, |
||||
timeDesc.minute, |
||||
timeDesc.second, |
||||
facility, |
||||
s_logSeverityToText[severity], |
||||
msg |
||||
); |
||||
|
||||
unsigned index = s_logSeverityToType[severity]; |
||||
s_logCrit[index].Enter(); |
||||
{ |
||||
// If day changed then write and flush file
|
||||
if (s_logTime[index].day != timeDesc.day) |
||||
FlushLogFile_CS(index, timeDesc); |
||||
// Otherwise if the buffer is full then write to file
|
||||
else if (s_logPos[index] + chars > s_logSize[index]) |
||||
WriteLogFile_CS(index, false); |
||||
|
||||
// Allocate log buffer if necessary
|
||||
if (!s_logBuf[index]) |
||||
AllocLogBuffer_CS(index); |
||||
|
||||
// Add new data to the log buffer
|
||||
memcpy(s_logBuf[index] + s_logPos[index], buffer, chars); |
||||
s_logPos[index] += chars; |
||||
|
||||
// Write, flush and close file immediately if this is a fatal error
|
||||
if (severity == kLogFatal) |
||||
WriteLogFile_CS(index, true); |
||||
|
||||
// Drop to debugger if this is an error msg and that option was specified
|
||||
if (s_breakOnErrors && severity >= kLogError) |
||||
DebugBreakIfDebuggerPresent(); |
||||
} |
||||
s_logCrit[index].Leave(); |
||||
|
||||
// Queue flush
|
||||
AsyncTimerUpdate(s_timer, kLogFlushMs, kAsyncTimerUpdateSetPriorityHigher); |
||||
#endif // ndef ACELOG_NO_LOG_FILES
|
||||
} |
||||
|
||||
//============================================================================
|
||||
void AsyncLogGetDirectory (wchar_t * dest, unsigned destChars) { |
||||
ASSERT(dest); |
||||
StrCopy(dest, s_directory, destChars); |
||||
} |
Loading…
Reference in new issue