1
0
forked from 0ad/0ad

thread-safety fixes to debug.cpp - most functions were already reentrant, just CAS-ified the assert suppression flags.

(NB: the debug_filter stuff remains non-reentrant because it's usually
only called from main() or at least the main thread)
closes #654

This was SVN commit r8543.
This commit is contained in:
janwas 2010-11-06 19:14:16 +00:00
parent 3407983e98
commit f45c74c368
3 changed files with 28 additions and 64 deletions

View File

@ -55,40 +55,6 @@ ERROR_ASSOCIATE(ERR::SYM_SINGLE_SYMBOL_LIMIT, L"Symbol has produced too much out
ERROR_ASSOCIATE(INFO::SYM_SUPPRESS_OUTPUT, L"Symbol was suppressed", -1);
// needed when writing crashlog
static const size_t LOG_CHARS = 16384;
wchar_t debug_log[LOG_CHARS];
wchar_t* debug_log_pos = debug_log;
// write to memory buffer (fast)
void debug_wprintf_mem(const wchar_t* fmt, ...)
{
const ssize_t charsLeft = (ssize_t)(LOG_CHARS - (debug_log_pos-debug_log));
debug_assert(charsLeft >= 0);
// potentially not enough room for the new string; throw away the
// older half of the log. we still protect against overflow below.
if(charsLeft < 512)
{
const size_t copySize = sizeof(wchar_t) * LOG_CHARS/2;
wchar_t* const middle = &debug_log[LOG_CHARS/2];
memcpy(debug_log, middle, copySize);
memset(middle, 0, copySize);
debug_log_pos -= LOG_CHARS/2; // don't assign middle (may leave gap)
}
// write into buffer (in-place)
va_list args;
va_start(args, fmt);
int len = vswprintf_s(debug_log_pos, charsLeft-2, fmt, args);
va_end(args);
debug_log_pos += len+2;
wcscpy_s(debug_log_pos-2, 3, L"\r\n");
}
// need to shoehorn printf-style variable params into
// the OutputDebugString call.
// - don't want to split into multiple calls - would add newlines to output.
@ -224,8 +190,6 @@ LibError debug_WriteCrashlog(const wchar_t* text)
// allow user to bundle whatever information they want
ah_bundle_logs(f);
fwprintf(f, L"Last known activity:\n\n %ls\n", debug_log);
fclose(f);
state = IDLE;
return INFO::OK;
@ -383,11 +347,11 @@ void debug_DisplayMessage(const wchar_t* caption, const wchar_t* msg)
// errors (e.g. caused by atexit handlers) to come up, possibly causing an
// infinite loop. hiding errors isn't good, but we assume that whoever clicked
// exit really doesn't want to see any more messages.
static bool isExiting;
static atomic_bool isExiting;
// this logic is applicable to any type of error. special cases such as
// suppressing certain expected WARN_ERRs are done there.
static bool ShouldSuppressError(u8* suppress)
static bool ShouldSuppressError(atomic_bool* suppress)
{
if(isExiting)
return true;
@ -412,7 +376,7 @@ static ErrorReaction CallDisplayError(const wchar_t* text, size_t flags)
return er;
}
static ErrorReaction PerformErrorReaction(ErrorReaction er, size_t flags, u8* suppress)
static ErrorReaction PerformErrorReaction(ErrorReaction er, size_t flags, atomic_bool* suppress)
{
const bool shouldHandleBreak = (flags & DE_MANUAL_BREAK) == 0;
@ -429,12 +393,13 @@ static ErrorReaction PerformErrorReaction(ErrorReaction er, size_t flags, u8* su
break;
case ER_SUPPRESS:
*suppress = DEBUG_SUPPRESS;
(void)cpu_CAS(suppress, 0, DEBUG_SUPPRESS);
er = ER_CONTINUE;
break;
case ER_EXIT:
isExiting = true; // see declaration
isExiting = 1; // see declaration
COMPILER_FENCE;
#if OS_WIN
// prevent (slow) heap reporting since we're exiting abnormally and
@ -451,7 +416,7 @@ static ErrorReaction PerformErrorReaction(ErrorReaction er, size_t flags, u8* su
ErrorReaction debug_DisplayError(const wchar_t* description,
size_t flags, void* context, const wchar_t* lastFuncToSkip,
const wchar_t* pathname, int line, const char* func,
u8* suppress)
atomic_bool* suppress)
{
// "suppressing" this error means doing nothing and returning ER_CONTINUE.
if(ShouldSuppressError(suppress))
@ -500,7 +465,7 @@ enum SkipStatus
{
INVALID, VALID, BUSY
};
static intptr_t skipStatus = INVALID; // cpu_CAS requires uintptr_t
static intptr_t skipStatus = INVALID;
static LibError errorToSkip;
static size_t numSkipped;
@ -547,7 +512,7 @@ static bool ShouldSkipError(LibError err)
}
ErrorReaction debug_OnError(LibError err, u8* suppress, const wchar_t* file, int line, const char* func)
ErrorReaction debug_OnError(LibError err, atomic_bool* suppress, const wchar_t* file, int line, const char* func)
{
if(ShouldSkipError(err))
return ER_CONTINUE;
@ -561,7 +526,7 @@ ErrorReaction debug_OnError(LibError err, u8* suppress, const wchar_t* file, int
}
ErrorReaction debug_OnAssertionFailure(const wchar_t* expr, u8* suppress, const wchar_t* file, int line, const char* func)
ErrorReaction debug_OnAssertionFailure(const wchar_t* expr, atomic_bool* suppress, const wchar_t* file, int line, const char* func)
{
void* context = 0;
const std::wstring lastFuncToSkip = L"debug_OnAssertionFailure";

View File

@ -105,6 +105,13 @@ enum DebugDisplayErrorFlags
DE_MANUAL_BREAK = 4
};
/**
* a bool that is reasonably certain to be set atomically.
* we cannot assume support for OpenMP (requires GCC 4.2) or C++0x,
* so we'll have to resort to intptr_t, cpu_CAS and COMPILER_FENCE.
**/
typedef volatile intptr_t atomic_bool;
/**
* value for suppress flag once set by debug_DisplayError.
* rationale: this value is fairly distinctive and helps when
@ -112,7 +119,7 @@ enum DebugDisplayErrorFlags
* initial value is 0 rather that another constant; this avoids
* allocating .rdata space.
**/
const u8 DEBUG_SUPPRESS = 0xAB;
const atomic_bool DEBUG_SUPPRESS = 0xAB;
/**
* choices offered by the shared error dialog
@ -170,7 +177,7 @@ enum ErrorReaction
* provides the storage. values: see DEBUG_SUPPRESS above.
* @return ErrorReaction (user's choice: continue running or stop?)
**/
LIB_API ErrorReaction debug_DisplayError(const wchar_t* description, size_t flags, void* context, const wchar_t* lastFuncToSkip, const wchar_t* file, int line, const char* func, u8* suppress);
LIB_API ErrorReaction debug_DisplayError(const wchar_t* description, size_t flags, void* context, const wchar_t* lastFuncToSkip, const wchar_t* file, int line, const char* func, atomic_bool* suppress);
/**
* convenience version, in case the advanced parameters aren't needed.
@ -201,8 +208,9 @@ LIB_API ErrorReaction debug_DisplayError(const wchar_t* description, size_t flag
* - filter changes only affect subsequent debug_*printf calls;
* output that didn't pass the filter is permanently discarded.
* - strings not starting with a tag are always displayed.
* - debug_filter_* can be called at any time and from the debugger.
* - debug_filter_* can be called at any time and from the debugger,
* but are not reentrant.
*
* in future, allow output with the given tag to proceed.
* no effect if already added.
**/
@ -227,15 +235,6 @@ LIB_API void debug_filter_clear();
**/
LIB_API bool debug_filter_allows(const wchar_t* text);
/**
* write to memory buffer (fast)
* used for "last activity" reporting in the crashlog.
*
* @param fmt Format string and varags; see printf.
**/
LIB_API void debug_wprintf_mem(const wchar_t* fmt, ...) WPRINTF_ARGS(1);
/**
* write an error description and all logs into crashlog.txt
* (in unicode format).
@ -273,7 +272,7 @@ LIB_API LibError debug_WriteCrashlog(const wchar_t* text);
**/
#define debug_assert(expr) \
STMT(\
static u8 suppress__;\
static atomic_bool suppress__;\
if(!(expr))\
{\
switch(debug_OnAssertionFailure(WIDEN(#expr), &suppress__, WIDEN(__FILE__), __LINE__, __func__))\
@ -302,7 +301,7 @@ STMT(\
**/
#define debug_warn(expr) \
STMT(\
static u8 suppress__;\
static atomic_bool suppress__;\
switch(debug_OnAssertionFailure(expr, &suppress__, WIDEN(__FILE__), __LINE__, __func__))\
{\
case ER_BREAK:\
@ -321,7 +320,7 @@ STMT(\
**/
#define DEBUG_WARN_ERR(err)\
STMT(\
static u8 suppress__;\
static atomic_bool suppress__;\
switch(debug_OnError(err, &suppress__, WIDEN(__FILE__), __LINE__, __func__))\
{\
case ER_BREAK:\
@ -344,7 +343,7 @@ STMT(\
* @param func name of the function containing it
* @return ErrorReaction (user's choice: continue running or stop?)
**/
LIB_API ErrorReaction debug_OnAssertionFailure(const wchar_t* assert_expr, u8* suppress, const wchar_t* file, int line, const char* func);
LIB_API ErrorReaction debug_OnAssertionFailure(const wchar_t* assert_expr, atomic_bool* suppress, const wchar_t* file, int line, const char* func);
/**
* called when a DEBUG_WARN_ERR indicates an error occurred;
@ -356,7 +355,7 @@ LIB_API ErrorReaction debug_OnAssertionFailure(const wchar_t* assert_expr, u8* s
* @param func name of the function containing it
* @return ErrorReaction (user's choice: continue running or stop?)
**/
LIB_API ErrorReaction debug_OnError(LibError err, u8* suppress, const wchar_t* file, int line, const char* func);
LIB_API ErrorReaction debug_OnError(LibError err, atomic_bool* suppress, const wchar_t* file, int line, const char* func);
/**

View File

@ -1022,7 +1022,7 @@ LibError ogl_tex_bind(Handle ht, size_t unit)
// we don't bother catching that and disabling texturing because a
// debug warning is raised anyway, and it's quite unlikely.
H_DEREF(ht, OglTex, ot);
ot->tmu = unit;
ot->tmu = (u8)unit;
// if 0, there's a problem in the OglTex reload/dtor logic.
// binding it results in whiteness, which can have many causes;