winit: rename register macros for more clarity
wdbg: cleanup, improve exception catcher (previously potentially failed if __try block came in non-main thread). required since wstartup no longer commandeers the entry point. winit, wstartup: update documentation This was SVN commit r5141.
This commit is contained in:
parent
92578ae553
commit
d802b73d94
@ -17,7 +17,7 @@
|
|||||||
#include "wutil.h"
|
#include "wutil.h"
|
||||||
#include "winit.h"
|
#include "winit.h"
|
||||||
|
|
||||||
WINIT_REGISTER_INIT_EARLY(wcpu_Init); // wcpu -> whrt
|
WINIT_REGISTER_EARLY_INIT(wcpu_Init); // wcpu -> whrt
|
||||||
|
|
||||||
static uint numProcessors = 0;
|
static uint numProcessors = 0;
|
||||||
|
|
||||||
|
@ -25,12 +25,7 @@
|
|||||||
#include "winit.h"
|
#include "winit.h"
|
||||||
#include "wutil.h"
|
#include "wutil.h"
|
||||||
|
|
||||||
WINIT_REGISTER_INIT_MAIN(wdbg_Init);
|
WINIT_REGISTER_EARLY_INIT(wdbg_Init); // registers exception handler
|
||||||
|
|
||||||
// used to prevent the vectored exception handler from taking charge when
|
|
||||||
// an exception is raised from the main thread (allows __try blocks to
|
|
||||||
// get control). latched in wdbg_Init.
|
|
||||||
static DWORD main_thread_id;
|
|
||||||
|
|
||||||
|
|
||||||
// protects the breakpoint helper thread.
|
// protects the breakpoint helper thread.
|
||||||
@ -45,43 +40,15 @@ static void unlock()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void debug_puts(const char* text)
|
static NT_TIB* get_tib()
|
||||||
{
|
{
|
||||||
OutputDebugStringA(text);
|
NT_TIB* tib;
|
||||||
}
|
__asm
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
// inform the debugger of the current thread's description, which it then
|
|
||||||
// displays instead of just the thread handle.
|
|
||||||
void wdbg_set_thread_name(const char* name)
|
|
||||||
{
|
|
||||||
// we pass information to the debugger via a special exception it
|
|
||||||
// swallows. if not running under one, bail now to avoid
|
|
||||||
// "first chance exception" warnings.
|
|
||||||
if(!IsDebuggerPresent())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// presented by Jay Bazuzi (from the VC debugger team) at TechEd 1999.
|
|
||||||
const struct ThreadNameInfo
|
|
||||||
{
|
{
|
||||||
DWORD type;
|
mov eax, fs:[NT_TIB.Self]
|
||||||
const char* name;
|
mov [tib], eax
|
||||||
DWORD thread_id; // any valid ID or -1 for current thread
|
|
||||||
DWORD flags;
|
|
||||||
}
|
|
||||||
info = { 0x1000, name, (DWORD)-1, 0 };
|
|
||||||
__try
|
|
||||||
{
|
|
||||||
RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info);
|
|
||||||
}
|
|
||||||
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
||||||
{
|
|
||||||
// if we get here, the debugger didn't handle the exception.
|
|
||||||
debug_warn("thread name hack doesn't work under this debugger");
|
|
||||||
}
|
}
|
||||||
|
return tib;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -143,7 +110,9 @@ void debug_heap_enable(DebugHeapChecks what)
|
|||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
// thread suspension
|
||||||
|
|
||||||
|
// suspend a thread, execute a user callback, revive the thread.
|
||||||
|
|
||||||
// to avoid deadlock, be VERY CAREFUL to avoid anything that may block,
|
// to avoid deadlock, be VERY CAREFUL to avoid anything that may block,
|
||||||
// including locks taken by the OS (e.g. malloc, GetProcAddress).
|
// including locks taken by the OS (e.g. malloc, GetProcAddress).
|
||||||
@ -207,11 +176,9 @@ static LibError call_while_suspended(WhileSuspendedFunc func, void* user_arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//-----------------------------------------------------------------------------
|
||||||
//
|
|
||||||
// breakpoints
|
// breakpoints
|
||||||
//
|
//-----------------------------------------------------------------------------
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// breakpoints are set by storing the address of interest in a
|
// breakpoints are set by storing the address of interest in a
|
||||||
// debug register and marking it 'enabled'.
|
// debug register and marking it 'enabled'.
|
||||||
@ -390,11 +357,8 @@ LibError debug_remove_all_breaks()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//-----------------------------------------------------------------------------
|
||||||
//
|
// analyze SEH exceptions
|
||||||
// exception handler
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// analyze exceptions; determine their type and locus
|
// analyze exceptions; determine their type and locus
|
||||||
@ -594,53 +558,59 @@ static void get_exception_locus(const EXCEPTION_POINTERS* ep,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// called* when an SEH exception was not caught by the app;
|
//-----------------------------------------------------------------------------
|
||||||
// provides detailed debugging information and exits.
|
// exception handler
|
||||||
// (via win.cpp!entry's __except or vectored_exception_handler; see below)
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
rationale:
|
||||||
|
we want to replace the OS "program error" dialog box because
|
||||||
|
it is not all too helpful in debugging. to that end, there are
|
||||||
|
5 ways to make sure unhandled SEH exceptions are caught:
|
||||||
|
- via WaitForDebugEvent; the app is run from a separate debugger process.
|
||||||
|
this complicates analysis, since the exception is in another
|
||||||
|
address space. also, we are basically implementing a full-featured
|
||||||
|
debugger - overkill.
|
||||||
|
- by wrapping all threads in __try (necessary since the handler chain
|
||||||
|
is in TLS). this can be done with the cooperation of wpthread,
|
||||||
|
but threads not under our control aren't covered.
|
||||||
|
- with a vectored exception handler. this works across threads, but
|
||||||
|
is never called when the process is being debugged (messing with
|
||||||
|
the PEB flag doesn't help; root cause is the Win32
|
||||||
|
KiUserExceptionDispatcher implementation). also, it's only available
|
||||||
|
on WinXP (unacceptable). finally, it is called before __try blocks,
|
||||||
|
so would receive expected/legitimate exceptions.
|
||||||
|
- by setting the per-process unhandled exception filter. as above,
|
||||||
|
this works across threads and isn't called while a debugger is active;
|
||||||
|
it is at least portable across Win32. unfortunately, some Win32 DLLs
|
||||||
|
appear to register their own handlers, so this isn't reliable.
|
||||||
|
- by hooking the exception dispatcher. this isn't future-proof.
|
||||||
|
|
||||||
|
wrapping all threads in a __try appears to be the best choice. however,
|
||||||
|
with wstartup no longer commandeering the exit point, we need to
|
||||||
|
retroactively install an SEH handler. this is done by directly
|
||||||
|
hooking into the TIB handler list.
|
||||||
|
|
||||||
|
since C++ exceptions are implemented via SEH, we can also catch those here;
|
||||||
|
it's nicer than a global try{} and avoids duplicating this code.
|
||||||
|
we can still get at the C++ information (std::exception.what()) by
|
||||||
|
examining the internal exception data structures. these are
|
||||||
|
compiler-specific, but haven't changed from VC5-VC7.1.
|
||||||
|
alternatively, _set_se_translator could be used to translate all
|
||||||
|
SEH exceptions to C++. this way is more reliable/documented, but has
|
||||||
|
several drawbacks:
|
||||||
|
- it wouldn't work at all in C programs,
|
||||||
|
- a new fat exception class would have to be created to hold the
|
||||||
|
SEH exception information (e.g. CONTEXT for a stack trace), and
|
||||||
|
- this information would not be available for C++ exceptions.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// called when an exception is detected (see below); provides detailed
|
||||||
|
// debugging information and exits.
|
||||||
//
|
//
|
||||||
// note: keep memory allocs and locking to an absolute minimum, because
|
// note: keep memory allocs and locking to an absolute minimum, because
|
||||||
// they may deadlock the process!
|
// they may deadlock the process!
|
||||||
//
|
|
||||||
// rationale:
|
|
||||||
// we want to replace the OS "program error" dialog box because
|
|
||||||
// it is not all too helpful in debugging. to that end, there are
|
|
||||||
// 5 ways to make sure unhandled SEH exceptions are caught:
|
|
||||||
// - via WaitForDebugEvent; the app is run from a separate debugger process.
|
|
||||||
// this complicates analysis, since the exception is in another
|
|
||||||
// address space. also, we are basically implementing a full-featured
|
|
||||||
// debugger - overkill.
|
|
||||||
// - by wrapping all threads in __try (necessary since the handler chain
|
|
||||||
// is in TLS). this is very difficult to guarantee.
|
|
||||||
// - with a vectored exception handler. this works across threads, but
|
|
||||||
// is never called when the process is being debugged
|
|
||||||
// (messing with the PEB flag doesn't help; root cause is the
|
|
||||||
// Win32 KiUserExceptionDispatcher implementation).
|
|
||||||
// worse, it's only available on WinXP (unacceptable).
|
|
||||||
// - by setting the per-process unhandled exception filter. as above,
|
|
||||||
// this works across threads and isn't called while a debugger is active;
|
|
||||||
// it is at least portable across Win32. unfortunately, some Win32 DLLs
|
|
||||||
// appear to register their own handlers, so this isn't reliable.
|
|
||||||
// - by hooking the exception dispatcher. this isn't future-proof.
|
|
||||||
//
|
|
||||||
// so, SNAFU. we compromise and register a regular __except filter at
|
|
||||||
// program entry point and add a vectored exception handler
|
|
||||||
// (if supported by the OS) to cover other threads.
|
|
||||||
// we steer clear of the process-wide unhandled exception filter,
|
|
||||||
// because it is not understood in what cases it can be overwritten;
|
|
||||||
// this precludes reliable use.
|
|
||||||
//
|
|
||||||
// since C++ exceptions are implemented via SEH, we can also catch those here;
|
|
||||||
// it's nicer than a global try{} and avoids duplicating this code.
|
|
||||||
// we can still get at the C++ information (std::exception.what()) by
|
|
||||||
// examining the internal exception data structures. these are
|
|
||||||
// compiler-specific, but haven't changed from VC5-VC7.1.
|
|
||||||
// alternatively, _set_se_translator could be used to translate all
|
|
||||||
// SEH exceptions to C++. this way is more reliable/documented, but has
|
|
||||||
// several drawbacks:
|
|
||||||
// - it wouldn't work at all in C programs,
|
|
||||||
// - a new fat exception class would have to be created to hold the
|
|
||||||
// SEH exception information (e.g. CONTEXT for a stack trace), and
|
|
||||||
// - this information would not be available for C++ exceptions.
|
|
||||||
LONG WINAPI wdbg_exception_filter(EXCEPTION_POINTERS* ep)
|
LONG WINAPI wdbg_exception_filter(EXCEPTION_POINTERS* ep)
|
||||||
{
|
{
|
||||||
// OutputDebugString raises an exception, which OUGHT to be swallowed
|
// OutputDebugString raises an exception, which OUGHT to be swallowed
|
||||||
@ -648,6 +618,11 @@ LONG WINAPI wdbg_exception_filter(EXCEPTION_POINTERS* ep)
|
|||||||
if(ep->ExceptionRecord->ExceptionCode == 0x40010006) // DBG_PRINTEXCEPTION_C
|
if(ep->ExceptionRecord->ExceptionCode == 0x40010006) // DBG_PRINTEXCEPTION_C
|
||||||
return EXCEPTION_CONTINUE_EXECUTION;
|
return EXCEPTION_CONTINUE_EXECUTION;
|
||||||
|
|
||||||
|
// if run in a debugger, let it handle exceptions (tends to be more
|
||||||
|
// convenient since it can bring up the crash location)
|
||||||
|
if(IsDebuggerPresent())
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
|
||||||
// note: we risk infinite recursion if someone raises an SEH exception
|
// note: we risk infinite recursion if someone raises an SEH exception
|
||||||
// from within this function. therefore, abort immediately if we have
|
// from within this function. therefore, abort immediately if we have
|
||||||
// already been called; the first error is the most important, anyway.
|
// already been called; the first error is the most important, anyway.
|
||||||
@ -693,39 +668,64 @@ LONG WINAPI wdbg_exception_filter(EXCEPTION_POINTERS* ep)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static LONG WINAPI vectored_exception_handler(EXCEPTION_POINTERS* ep)
|
//-----------------------------------------------------------------------------
|
||||||
|
// install SEH exception handler
|
||||||
|
|
||||||
|
typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE)(_EXCEPTION_RECORD* ExceptionRecord, PVOID EstablisherFrame, _CONTEXT* ContextRecord, PVOID DispatcherContext);
|
||||||
|
|
||||||
|
struct _EXCEPTION_REGISTRATION_RECORD
|
||||||
{
|
{
|
||||||
// since we're called from the vectored handler chain,
|
_EXCEPTION_REGISTRATION_RECORD* Next;
|
||||||
// ignore exceptions from the main thread. this allows
|
PEXCEPTION_ROUTINE Handler;
|
||||||
// __try blocks to take charge; entry() catches all exceptions with a
|
};
|
||||||
// standard filter and relays them to wdbg_exception_filter.
|
|
||||||
if(main_thread_id == GetCurrentThreadId())
|
static bool IsUnwinding(DWORD exceptionFlags)
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
{
|
||||||
return wdbg_exception_filter(ep);
|
return (exceptionFlags & 2) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EXCEPTION_DISPOSITION ExceptionHandler(_EXCEPTION_RECORD* ExceptionRecord, PVOID EstablisherFrame, _CONTEXT* ContextRecord, PVOID DispatcherContext)
|
||||||
|
{
|
||||||
|
if(!IsUnwinding(ExceptionRecord->ExceptionFlags))
|
||||||
|
{
|
||||||
|
EXCEPTION_POINTERS ep;
|
||||||
|
ep.ExceptionRecord = ExceptionRecord;
|
||||||
|
ep.ContextRecord = ContextRecord;
|
||||||
|
if(wdbg_exception_filter(&ep) == EXCEPTION_CONTINUE_EXECUTION)
|
||||||
|
return ExceptionContinueExecution;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExceptionContinueSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cassert(WDBG_XRR_STORAGE_SIZE >= sizeof(_EXCEPTION_REGISTRATION_RECORD));
|
||||||
|
|
||||||
|
void wdbg_InstallExceptionHandler(void* xrrStorage)
|
||||||
|
{
|
||||||
|
_EXCEPTION_REGISTRATION_RECORD* xrr = (_EXCEPTION_REGISTRATION_RECORD*)xrrStorage;
|
||||||
|
|
||||||
|
// add to front of handler list
|
||||||
|
NT_TIB* tib = get_tib();
|
||||||
|
xrr->Handler = ExceptionHandler;
|
||||||
|
xrr->Next = tib->ExceptionList;
|
||||||
|
tib->ExceptionList = xrr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
static LibError wdbg_Init(void)
|
static LibError wdbg_Init(void)
|
||||||
{
|
{
|
||||||
// see decl
|
static _EXCEPTION_REGISTRATION_RECORD xrrStorage;
|
||||||
main_thread_id = GetCurrentThreadId();
|
wdbg_InstallExceptionHandler(&xrrStorage);
|
||||||
|
|
||||||
// add vectored exception handler (if supported by the OS).
|
|
||||||
// see rationale above.
|
|
||||||
#if _WIN32_WINNT >= 0x0500 // this is how winbase.h tests for it
|
|
||||||
const HMODULE hKernel32Dll = LoadLibrary("kernel32.dll");
|
|
||||||
PVOID (WINAPI *pAddVectoredExceptionHandler)(IN ULONG FirstHandler, IN PVECTORED_EXCEPTION_HANDLER VectoredHandler);
|
|
||||||
*(void**)&pAddVectoredExceptionHandler = GetProcAddress(hKernel32Dll, "AddVectoredExceptionHandler");
|
|
||||||
FreeLibrary(hKernel32Dll);
|
|
||||||
if(pAddVectoredExceptionHandler)
|
|
||||||
pAddVectoredExceptionHandler(TRUE, vectored_exception_handler);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return INFO::OK;
|
return INFO::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// stateless functions
|
||||||
|
|
||||||
// return 1 if the pointer appears to be totally bogus, otherwise 0.
|
// return 1 if the pointer appears to be totally bogus, otherwise 0.
|
||||||
// this check is not authoritative (the pointer may be "valid" but incorrect)
|
// this check is not authoritative (the pointer may be "valid" but incorrect)
|
||||||
@ -766,17 +766,6 @@ bool debug_is_code_ptr(void* p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static NT_TIB* get_tib()
|
|
||||||
{
|
|
||||||
NT_TIB* tib;
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax, fs:[NT_TIB.Self]
|
|
||||||
mov [tib], eax
|
|
||||||
}
|
|
||||||
return tib;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool debug_is_stack_ptr(void* p)
|
bool debug_is_stack_ptr(void* p)
|
||||||
{
|
{
|
||||||
uintptr_t addr = (uintptr_t)p;
|
uintptr_t addr = (uintptr_t)p;
|
||||||
@ -795,6 +784,38 @@ bool debug_is_stack_ptr(void* p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void debug_puts(const char* text)
|
||||||
|
{
|
||||||
|
OutputDebugStringA(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// inform the debugger of the current thread's description, which it then
|
||||||
|
// displays instead of just the thread handle.
|
||||||
|
void wdbg_set_thread_name(const char* name)
|
||||||
|
{
|
||||||
|
// we pass information to the debugger via a special exception it
|
||||||
|
// swallows. if not running under one, bail now to avoid
|
||||||
|
// "first chance exception" warnings.
|
||||||
|
if(!IsDebuggerPresent())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// presented by Jay Bazuzi (from the VC debugger team) at TechEd 1999.
|
||||||
|
const struct ThreadNameInfo
|
||||||
|
{
|
||||||
|
DWORD type;
|
||||||
|
const char* name;
|
||||||
|
DWORD thread_id; // any valid ID or -1 for current thread
|
||||||
|
DWORD flags;
|
||||||
|
}
|
||||||
|
info = { 0x1000, name, (DWORD)-1, 0 };
|
||||||
|
__try
|
||||||
|
{
|
||||||
|
RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info);
|
||||||
|
}
|
||||||
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
||||||
|
{
|
||||||
|
// if we get here, the debugger didn't handle the exception.
|
||||||
|
debug_warn("thread name hack doesn't work under this debugger");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,11 +17,19 @@
|
|||||||
# error "port this or define to implementation function"
|
# error "port this or define to implementation function"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// internal use only:
|
|
||||||
extern void wdbg_set_thread_name(const char* name);
|
extern void wdbg_set_thread_name(const char* name);
|
||||||
|
|
||||||
// see rationale at definition.
|
|
||||||
struct _EXCEPTION_POINTERS;
|
const size_t WDBG_XRR_STORAGE_SIZE = 16;
|
||||||
extern long __stdcall wdbg_exception_filter(_EXCEPTION_POINTERS* ep);
|
|
||||||
|
/**
|
||||||
|
* install an SEH handler for the current thread.
|
||||||
|
*
|
||||||
|
* @param xrrStorage - storage used to hold the SEH handler node that's
|
||||||
|
* entered into the TIB's list. must be at least WDBG_XRR_STORAGE_SIZE bytes
|
||||||
|
* and remain valid over the lifetime of the thread.
|
||||||
|
**/
|
||||||
|
void wdbg_InstallExceptionHandler(void* xrrStorage);
|
||||||
|
|
||||||
#endif // #ifndef INCLUDED_WDBG
|
#endif // #ifndef INCLUDED_WDBG
|
||||||
|
@ -37,8 +37,8 @@
|
|||||||
#pragma comment(lib, "oleaut32.lib") // VariantChangeType
|
#pragma comment(lib, "oleaut32.lib") // VariantChangeType
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
WINIT_REGISTER_INIT_MAIN(wdbg_sym_Init);
|
WINIT_REGISTER_MAIN_INIT(wdbg_sym_Init);
|
||||||
WINIT_REGISTER_SHUTDOWN_MAIN(wdbg_sym_Shutdown);
|
WINIT_REGISTER_MAIN_SHUTDOWN(wdbg_sym_Shutdown);
|
||||||
|
|
||||||
// note: it is safe to use debug_assert/debug_warn/CHECK_ERR even during a
|
// note: it is safe to use debug_assert/debug_warn/CHECK_ERR even during a
|
||||||
// stack trace (which is triggered by debug_assert et al. in app code) because
|
// stack trace (which is triggered by debug_assert et al. in app code) because
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
#include "winit.h"
|
#include "winit.h"
|
||||||
#include "wutil.h"
|
#include "wutil.h"
|
||||||
|
|
||||||
WINIT_REGISTER_INIT_MAIN(wdir_watch_Init);
|
WINIT_REGISTER_MAIN_INIT(wdir_watch_Init);
|
||||||
WINIT_REGISTER_SHUTDOWN_MAIN(wdir_watch_Shutdown);
|
WINIT_REGISTER_MAIN_SHUTDOWN(wdir_watch_Shutdown);
|
||||||
|
|
||||||
// rationale for polling:
|
// rationale for polling:
|
||||||
// much simpler than pure asynchronous notification: no need for a
|
// much simpler than pure asynchronous notification: no need for a
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#include "win.h"
|
#include "win.h"
|
||||||
#include "winit.h"
|
#include "winit.h"
|
||||||
|
|
||||||
WINIT_REGISTER_SHUTDOWN_LATE2(wdll_Shutdown); // last - DLLs are unloaded here
|
WINIT_REGISTER_LATE_SHUTDOWN2(wdll_Shutdown); // last - DLLs are unloaded here
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// delay loading (modified from VC7 DelayHlp.cpp and DelayImp.h)
|
// delay loading (modified from VC7 DelayHlp.cpp and DelayImp.h)
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
|
|
||||||
#include "counter.h"
|
#include "counter.h"
|
||||||
|
|
||||||
WINIT_REGISTER_INIT_EARLY2(whrt_Init); // whrt -> wtime
|
WINIT_REGISTER_EARLY_INIT2(whrt_Init); // whrt -> wtime
|
||||||
WINIT_REGISTER_SHUTDOWN_LATE(whrt_Shutdown);
|
WINIT_REGISTER_LATE_SHUTDOWN(whrt_Shutdown);
|
||||||
|
|
||||||
|
|
||||||
namespace ERR
|
namespace ERR
|
||||||
|
@ -11,9 +11,10 @@
|
|||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
#include "winit.h"
|
#include "winit.h"
|
||||||
|
|
||||||
#include "win.h" // GetTickCount
|
#include "win.h" // GetTickCount for quick'n dirty timing
|
||||||
|
|
||||||
// see http://blogs.msdn.com/larryosterman/archive/2004/09/27/234840.aspx
|
// see http://blogs.msdn.com/larryosterman/archive/2004/09/27/234840.aspx
|
||||||
|
// for discussion of a similar mechanism.
|
||||||
|
|
||||||
|
|
||||||
typedef LibError (*PfnLibErrorVoid)(void);
|
typedef LibError (*PfnLibErrorVoid)(void);
|
||||||
|
@ -16,26 +16,35 @@
|
|||||||
Overview
|
Overview
|
||||||
--------
|
--------
|
||||||
|
|
||||||
participating modules store function pointer(s) to their init and/or
|
This facility allows registering init and shutdown functions with only
|
||||||
shutdown function in a specific COFF section. the sections are
|
one line of code and zero runtime overhead. It provides for dependencies
|
||||||
|
between modules, allowing groups of functions to run before others.
|
||||||
|
|
||||||
|
|
||||||
|
Details
|
||||||
|
-------
|
||||||
|
|
||||||
|
Participating modules store function pointer(s) to their init and/or
|
||||||
|
shutdown function in a specific COFF section. The sections are
|
||||||
grouped according to the desired notification and the order in which
|
grouped according to the desired notification and the order in which
|
||||||
functions are to be called (useful if one module depends on another).
|
functions are to be called (useful if one module depends on another).
|
||||||
they are then gathered by the linker and arranged in alphabetical order.
|
They are then gathered by the linker and arranged in alphabetical order.
|
||||||
placeholder variables in the sections indicate where the series of
|
Placeholder variables in the sections indicate where the series of
|
||||||
functions begins and ends for a given notification time.
|
functions begins and ends for a given notification time.
|
||||||
at runtime, all of the function pointers between the markers are invoked.
|
At runtime, all of the function pointers between the markers are invoked.
|
||||||
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
||||||
WINIT_REGISTER_INIT_MAIN(wtime_Init); // whrt -> wtime
|
(at file scope:)
|
||||||
|
WINIT_REGISTER_MAIN_INIT(InitCallback);
|
||||||
|
|
||||||
|
|
||||||
Rationale
|
Rationale
|
||||||
---------
|
---------
|
||||||
|
|
||||||
several methods of module init are possible: (see Large Scale C++ Design)
|
Several methods of module init are possible: (see Large Scale C++ Design)
|
||||||
- on-demand initialization: each exported function would have to check
|
- on-demand initialization: each exported function would have to check
|
||||||
if init already happened. that would be brittle and hard to verify.
|
if init already happened. that would be brittle and hard to verify.
|
||||||
- singleton: variant of the above, but not applicable to a
|
- singleton: variant of the above, but not applicable to a
|
||||||
@ -56,15 +65,15 @@ several methods of module init are possible: (see Large Scale C++ Design)
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// section declarations
|
// section declarations
|
||||||
|
|
||||||
// section names are of the format "WINIT${type}{group}".
|
// section names are of the format "WINIT${type}{group}".
|
||||||
// {type} is I for initialization- or S for shutdown functions.
|
// {type} is I for initialization- or S for shutdown functions.
|
||||||
// {group} is [0, 9] - see below.
|
// {group} is [0, 9] - see below.
|
||||||
// note: __declspec(allocate) requires sections to be 'declared'
|
// note: __declspec(allocate) requires declaring segments in advance via
|
||||||
// before using them.
|
// #pragma section.
|
||||||
|
|
||||||
#pragma section("WINIT$I$", read)
|
#pragma section("WINIT$I$", read)
|
||||||
#pragma section("WINIT$I0", read)
|
#pragma section("WINIT$I0", read)
|
||||||
#pragma section("WINIT$I1", read)
|
#pragma section("WINIT$I1", read)
|
||||||
@ -81,6 +90,7 @@ several methods of module init are possible: (see Large Scale C++ Design)
|
|||||||
#pragma section("WINIT$SZ", read)
|
#pragma section("WINIT$SZ", read)
|
||||||
#pragma comment(linker, "/merge:WINIT=.rdata")
|
#pragma comment(linker, "/merge:WINIT=.rdata")
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Function groups
|
// Function groups
|
||||||
|
|
||||||
@ -90,14 +100,14 @@ several methods of module init are possible: (see Large Scale C++ Design)
|
|||||||
// (this is because the linker sorts sections alphabetically but doesn't
|
// (this is because the linker sorts sections alphabetically but doesn't
|
||||||
// specify the order in which object files are processed.)
|
// specify the order in which object files are processed.)
|
||||||
|
|
||||||
// register a function to be called at the given time.
|
// these macros register a function to be called at the given time.
|
||||||
// usage: invoke at file scope, passing a function identifier/symbol.
|
// usage: invoke at file scope, passing a function identifier/symbol.
|
||||||
// rationale:
|
// rationale:
|
||||||
// - __declspec(allocate) requires section declarations, but allows users to
|
// - __declspec(allocate) requires section declarations, but allows users to
|
||||||
// write only one line (instead of needing an additional #pragma data_seg)
|
// write only one line (instead of needing an additional #pragma data_seg)
|
||||||
// - fixed groups instead of passing a group number are more clear and
|
// - fixed groups instead of passing a group number are more clear and
|
||||||
// encourage thinking about init order. (__declspec(allocate) requires
|
// encourage thinking about init order. (__declspec(allocate) requires
|
||||||
// a single string literal anyway and doesn't support merging)
|
// a single string literal anyway and doesn't support string merging)
|
||||||
// - why EXTERN_C and __pragma? VC8's link-stage optimizer believes
|
// - why EXTERN_C and __pragma? VC8's link-stage optimizer believes
|
||||||
// the static function pointers defined by WINIT_REGISTER_* to be unused;
|
// the static function pointers defined by WINIT_REGISTER_* to be unused;
|
||||||
// unless action is taken, they would be removed. to prevent this, we
|
// unless action is taken, they would be removed. to prevent this, we
|
||||||
@ -107,29 +117,29 @@ several methods of module init are possible: (see Large Scale C++ Design)
|
|||||||
|
|
||||||
// very early init; must not fail, since error handling code *crashes*
|
// very early init; must not fail, since error handling code *crashes*
|
||||||
// if called before these have completed.
|
// if called before these have completed.
|
||||||
#define WINIT_REGISTER_INIT_CRITICAL(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I0")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
#define WINIT_REGISTER_CRITICAL_INIT(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I0")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
||||||
|
|
||||||
// meant for modules with dependents but whose init is complicated and may
|
// meant for modules with dependents but whose init is complicated and may
|
||||||
// raise error/warning messages (=> can't go in WINIT_REGISTER_INIT_CRITICAL)
|
// raise error/warning messages (=> can't go in WINIT_REGISTER_CRITICAL_INIT)
|
||||||
#define WINIT_REGISTER_INIT_EARLY(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I1")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
#define WINIT_REGISTER_EARLY_INIT(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I1")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
||||||
|
|
||||||
// available for dependents of WINIT_REGISTER_INIT_EARLY-modules that
|
// available for dependents of WINIT_REGISTER_EARLY_INIT-modules that
|
||||||
// must still come before WINIT_REGISTER_INIT_MAIN.
|
// must still come before WINIT_REGISTER_MAIN_INIT.
|
||||||
#define WINIT_REGISTER_INIT_EARLY2(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I2")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
#define WINIT_REGISTER_EARLY_INIT2(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I2")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
||||||
|
|
||||||
// most modules will go here unless they are often used or
|
// most modules will go here unless they are often used or
|
||||||
// have many dependents.
|
// have many dependents.
|
||||||
#define WINIT_REGISTER_INIT_MAIN(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I6")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
#define WINIT_REGISTER_MAIN_INIT(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I6")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
||||||
|
|
||||||
// available for any modules that may need to come after
|
// available for any modules that may need to come after
|
||||||
// WINIT_REGISTER_INIT_MAIN (unlikely)
|
// WINIT_REGISTER_MAIN_INIT (unlikely)
|
||||||
#define WINIT_REGISTER_INIT_LATE(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I7")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
#define WINIT_REGISTER_LATE_INIT(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$I7")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
||||||
|
|
||||||
#define WINIT_REGISTER_SHUTDOWN_EARLY(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S0")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
#define WINIT_REGISTER_EARLY_SHUTDOWN(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S0")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
||||||
#define WINIT_REGISTER_SHUTDOWN_EARLY2(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S1")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
#define WINIT_REGISTER_EARLY_SHUTDOWN2(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S1")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
||||||
#define WINIT_REGISTER_SHUTDOWN_MAIN(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S6")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
#define WINIT_REGISTER_MAIN_SHUTDOWN(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S6")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
||||||
#define WINIT_REGISTER_SHUTDOWN_LATE(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S7")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
#define WINIT_REGISTER_LATE_SHUTDOWN(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S7")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
||||||
#define WINIT_REGISTER_SHUTDOWN_LATE2(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S8")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
#define WINIT_REGISTER_LATE_SHUTDOWN2(func) static LibError func(void); EXTERN_C __declspec(allocate("WINIT$S8")) LibError (*p##func)(void) = func; __pragma(comment(linker, "/include:_p"#func))
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
#include "lib/sysdep/cpu.h"
|
#include "lib/sysdep/cpu.h"
|
||||||
#include "lib/bits.h"
|
#include "lib/bits.h"
|
||||||
|
|
||||||
WINIT_REGISTER_INIT_MAIN(waio_Init);
|
WINIT_REGISTER_MAIN_INIT(waio_Init);
|
||||||
WINIT_REGISTER_SHUTDOWN_MAIN(waio_Shutdown);
|
WINIT_REGISTER_MAIN_SHUTDOWN(waio_Shutdown);
|
||||||
|
|
||||||
|
|
||||||
static void lock()
|
static void lock()
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#include "crt_posix.h" // _getcwd
|
#include "crt_posix.h" // _getcwd
|
||||||
#include "lib/bits.h"
|
#include "lib/bits.h"
|
||||||
|
|
||||||
WINIT_REGISTER_INIT_CRITICAL(wposix_Init); // wposix -> error handling
|
WINIT_REGISTER_CRITICAL_INIT(wposix_Init); // wposix -> error handling
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// sysconf
|
// sysconf
|
||||||
|
@ -469,14 +469,10 @@ static unsigned __stdcall thread_start(void* param)
|
|||||||
void* arg = func_and_arg->arg;
|
void* arg = func_and_arg->arg;
|
||||||
win_free(param);
|
win_free(param);
|
||||||
|
|
||||||
void* ret = (void*)-1;
|
u8 xrrStorage[WDBG_XRR_STORAGE_SIZE];
|
||||||
__try
|
wdbg_InstallExceptionHandler(xrrStorage);
|
||||||
{
|
|
||||||
ret = func(arg);
|
void* ret = func(arg);
|
||||||
}
|
|
||||||
__except(wdbg_exception_filter(GetExceptionInformation()))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
call_tls_dtors();
|
call_tls_dtors();
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
#pragma comment(lib, "ws2_32.lib")
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
WINIT_REGISTER_INIT_MAIN(wsock_Init);
|
WINIT_REGISTER_MAIN_INIT(wsock_Init);
|
||||||
WINIT_REGISTER_SHUTDOWN_MAIN(wsock_Shutdown);
|
WINIT_REGISTER_MAIN_SHUTDOWN(wsock_Shutdown);
|
||||||
|
|
||||||
uint16_t htons(uint16_t s)
|
uint16_t htons(uint16_t s)
|
||||||
{
|
{
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#include "lib/sysdep/cpu.h" // cpu_i64FromDouble
|
#include "lib/sysdep/cpu.h" // cpu_i64FromDouble
|
||||||
#include "lib/sysdep/win/whrt/whrt.h"
|
#include "lib/sysdep/win/whrt/whrt.h"
|
||||||
|
|
||||||
WINIT_REGISTER_INIT_MAIN(wtime_Init); // whrt -> wtime
|
WINIT_REGISTER_MAIN_INIT(wtime_Init); // whrt -> wtime
|
||||||
|
|
||||||
// NT system time and FILETIME are hectonanoseconds since Jan. 1, 1601 UTC.
|
// NT system time and FILETIME are hectonanoseconds since Jan. 1, 1601 UTC.
|
||||||
// SYSTEMTIME is a struct containing month, year, etc.
|
// SYSTEMTIME is a struct containing month, year, etc.
|
||||||
|
@ -13,46 +13,76 @@
|
|||||||
|
|
||||||
#include "winit.h"
|
#include "winit.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
Shutdown
|
||||||
// shutdown
|
--------
|
||||||
|
|
||||||
// note: the alternative of using atexit has two disadvantages.
|
our shutdown function must be called after static C++ dtors, since they
|
||||||
// - the call to atexit must come after _cinit, which means we'd need to be
|
may use wpthread and wtime functions. how to accomplish this?
|
||||||
// called manually from main (see discussion on init below)
|
|
||||||
// - other calls to atexit from ctors or hidden compiler-generated init code
|
|
||||||
// for static objects would cause those handlers to be called after ours,
|
|
||||||
// which may cause shutdown order bugs.
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
hooking ExitProcess is one way of ensuring we're the very last bit of
|
||||||
// init
|
code to be called. this has several big problems, though:
|
||||||
|
- if other exit paths (such as CorExitProcess) are added by MS and
|
||||||
|
triggered via external code injected into our process, we miss our cue
|
||||||
|
to shut down. one annoying consequence would be that the Aken driver
|
||||||
|
remains loaded. however, users also can terminate our process at any
|
||||||
|
time, so we need to safely handle lack of shutdown anyway.
|
||||||
|
- IAT hooking breaks if kernel32 is delay-loaded (who knows what
|
||||||
|
people are going to do..)
|
||||||
|
- Detours-style trampolines are nonportable (the Detours library currently
|
||||||
|
only ships with code to handle IA-32) and quite risky, since antivirus
|
||||||
|
programs may flag this activity.
|
||||||
|
|
||||||
// the init functions need to be called before any use of Windows-specific
|
having all exit paths call our shutdown and then _cexit would work,
|
||||||
// code. in particular, static ctors may use whrt or wpthread, so we ought to
|
provided we actually find and control all of them (unhandled exceptions
|
||||||
// be initialized before them as well.
|
and falling off the end of the last thread are non-obvious examples).
|
||||||
//
|
that aside, it'd require changes to calling code, and we're trying to
|
||||||
// one possibility is using WinMain as the entry point, and then calling the
|
keep this mess hidden and transparent to users.
|
||||||
// application's main(), but this is expressly forbidden by the C standard.
|
|
||||||
// VC apparently makes use of this and changes its calling convention.
|
the remaining alternative is to use atexit. this approach has the
|
||||||
// if we call it, everything appears to work but stack traces in
|
advantage of being covered by the CRT runtime checks and leak detector,
|
||||||
// release mode are incorrect (symbol address is off by 4).
|
because those are shut down after the atexit handlers are called.
|
||||||
//
|
however, it does require that our shutdown callback to be the very last,
|
||||||
// another alternative is re#defining the app's main function to app_main,
|
i.e. registered first. fortunately, the init stage can guarantee this.
|
||||||
// having the OS call our main, and then dispatching to app_main.
|
|
||||||
// however, this leads to trouble when another library (e.g. SDL) wants to
|
|
||||||
// do the same.
|
Init
|
||||||
// moreover, this file is compiled into a static library and used both for
|
----
|
||||||
// the 0ad executable as well as the separate self-test. this means
|
|
||||||
// we can't enable the main() hook for one and disable it in the other.
|
For the same reasons as above, our init really should happen before static
|
||||||
//
|
C++ ctors are called.
|
||||||
// requiring users to call us at the beginning of main is brittle in general,
|
|
||||||
// comes after static ctors, and is difficult to achieve in external code
|
using WinMain as the entry point and then calling the application's main()
|
||||||
// such as the (automatically generated) self-test.
|
doesn't satisy the above requirement, and is expressly forbidden by ANSI C.
|
||||||
//
|
(VC apparently makes use of this and changes its calling convention.
|
||||||
// commandeering the entry point, doing init there and then calling
|
if we call it, everything appears to work but stack traces in release
|
||||||
// mainCRTStartup would work, but doesn't help with shutdown - additional
|
mode are incorrect, since symbol addresses are off by 4 bytes.)
|
||||||
// measures are required (see above). note that this approach means we're
|
|
||||||
// initialized before _cinit, denying the use of non-stateless CRT functions.
|
another alternative is re#defining the app's main function to app_main,
|
||||||
|
having the OS call our main, and then dispatching to app_main.
|
||||||
|
however, this leads to trouble when another library (e.g. SDL) wants to
|
||||||
|
do the same.
|
||||||
|
moreover, this file is compiled into a static library and used both for
|
||||||
|
the 0ad executable as well as the separate self-test. this means
|
||||||
|
we can't enable the main() hook for one and disable it in the other.
|
||||||
|
|
||||||
|
requiring users to call us at the beginning of main is brittle in general,
|
||||||
|
comes after static ctors, and is difficult to achieve in external code
|
||||||
|
such as the (automatically generated) self-test.
|
||||||
|
|
||||||
|
commandeering the entry point, doing init there and then calling
|
||||||
|
mainCRTStartup would work, but doesn't allow the use of atexit for shutdown
|
||||||
|
(nor any other non-stateless CRT functions to be called during init).
|
||||||
|
|
||||||
|
the way out is to have an init-and-call-atexit function triggered by means
|
||||||
|
of the CRT init mechanism. we arrange init order such that this happens
|
||||||
|
before static C++ ctors, thus meeting all of the above requirements.
|
||||||
|
(note: this is possible because crtexe.c and its .CRT$XI and .CRT$XC
|
||||||
|
sections holding static initializers and ctors are linked into the
|
||||||
|
application, not the CRT DLL.)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
EXTERN_C void InitAndRegisterShutdown()
|
EXTERN_C void InitAndRegisterShutdown()
|
||||||
@ -65,25 +95,3 @@ EXTERN_C void InitAndRegisterShutdown()
|
|||||||
EXTERN_C void(*pInitAndRegisterShutdown)() = InitAndRegisterShutdown;
|
EXTERN_C void(*pInitAndRegisterShutdown)() = InitAndRegisterShutdown;
|
||||||
#pragma comment(linker, "/include:_pInitAndRegisterShutdown")
|
#pragma comment(linker, "/include:_pInitAndRegisterShutdown")
|
||||||
#pragma data_seg()
|
#pragma data_seg()
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// SEH wrapper
|
|
||||||
|
|
||||||
#include "wdbg.h" // wdbg_exception_filter
|
|
||||||
|
|
||||||
typedef int(*PfnIntVoid)(void);
|
|
||||||
|
|
||||||
static int RunWithinTryBlock(PfnIntVoid func)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
//__try
|
|
||||||
{
|
|
||||||
ret = func();
|
|
||||||
}
|
|
||||||
//__except(wdbg_exception_filter(GetExceptionInformation()))
|
|
||||||
{
|
|
||||||
ret = -1;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
@ -12,9 +12,9 @@
|
|||||||
// be called at the appropriate times.
|
// be called at the appropriate times.
|
||||||
//
|
//
|
||||||
// the current implementation manages to trigger winit initialization
|
// the current implementation manages to trigger winit initialization
|
||||||
// between CRT init and static C++ ctors. that means wpthread etc. APIs
|
// in-between calls to CRT init and the static C++ ctors. that means
|
||||||
// are safe to use from the ctors and also that winit initializers are
|
// wpthread etc. APIs are safe to use from ctors, and winit initializers
|
||||||
// allowed to use non-stateless CRT functions such as atexit.
|
// are allowed to use non-stateless CRT functions such as atexit.
|
||||||
|
|
||||||
#ifndef INCLUDED_WSTARTUP
|
#ifndef INCLUDED_WSTARTUP
|
||||||
#define INCLUDED_WSTARTUP
|
#define INCLUDED_WSTARTUP
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
#include "win.h"
|
#include "win.h"
|
||||||
#include "winit.h"
|
#include "winit.h"
|
||||||
|
|
||||||
WINIT_REGISTER_INIT_EARLY(wutil_Init);
|
WINIT_REGISTER_EARLY_INIT(wutil_Init);
|
||||||
WINIT_REGISTER_SHUTDOWN_LATE(wutil_Shutdown);
|
WINIT_REGISTER_LATE_SHUTDOWN(wutil_Shutdown);
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// safe allocator
|
// safe allocator
|
||||||
|
Loading…
Reference in New Issue
Block a user