diff --git a/source/lib/adts.h b/source/lib/adts.h index 9540864628..1d6fc1425f 100644 --- a/source/lib/adts.h +++ b/source/lib/adts.h @@ -332,7 +332,7 @@ public: head = (head + 1) % n; } else - debug_warn("underflow"); + debug_assert(0); // underflow } class iterator diff --git a/source/lib/allocators/allocators.cpp b/source/lib/allocators/allocators.cpp index 4482ac0cbe..8db9ab6617 100644 --- a/source/lib/allocators/allocators.cpp +++ b/source/lib/allocators/allocators.cpp @@ -122,7 +122,7 @@ void single_free(void* storage, volatile uintptr_t* in_use_flag, void* p) // ok, flag has been reset to 0 } else - debug_warn("in_use_flag out of sync (double free?)"); + debug_assert(0); // in_use_flag out of sync (double free?) } // was allocated from heap else diff --git a/source/lib/allocators/allocators.h b/source/lib/allocators/allocators.h index 900c5a3071..b32ed58466 100644 --- a/source/lib/allocators/allocators.h +++ b/source/lib/allocators/allocators.h @@ -36,7 +36,7 @@ * @param unaligned_size minimum size [bytes] to allocate. * @return page-aligned and -padded memory or 0 on error / out of memory. **/ -extern void* page_aligned_alloc(size_t unaligned_size); +LIB_API void* page_aligned_alloc(size_t unaligned_size); /** * free a previously allocated page-aligned region. @@ -44,10 +44,10 @@ extern void* page_aligned_alloc(size_t unaligned_size); * @param p exact value returned from page_aligned_alloc * @param size exact value passed to page_aligned_alloc **/ -extern void page_aligned_free(void* p, size_t unaligned_size); +LIB_API void page_aligned_free(void* p, size_t unaligned_size); -// adapter that allows calling page_aligned_free as a boost::shared_ptr deleter. +// adapter that allows calling page_aligned_free as a shared_ptr deleter. class PageAlignedDeleter { public: @@ -86,7 +86,7 @@ private: * @return 0 if out of memory, otherwise matrix that should be cast to * type** (sizeof(type) == el_size). must be freed via matrix_free. **/ -extern void** matrix_alloc(uint cols, uint rows, size_t el_size); +LIB_API void** matrix_alloc(uint cols, uint rows, size_t el_size); /** * free the given matrix. @@ -95,7 +95,7 @@ extern void** matrix_alloc(uint cols, uint rows, size_t el_size); * callers will likely want to pass variables of a different type * (e.g. int**); they must be cast to void**. **/ -extern void matrix_free(void** matrix); +LIB_API void matrix_free(void** matrix); //----------------------------------------------------------------------------- @@ -120,7 +120,7 @@ extern void matrix_free(void** matrix); * @return allocated memory (typically = , but falls back to * malloc if that's in-use), or 0 (with warning) if out of memory. **/ -extern void* single_calloc(void* storage, volatile uintptr_t* in_use_flag, size_t size); +LIB_API void* single_calloc(void* storage, volatile uintptr_t* in_use_flag, size_t size); /** * Free a memory block that had been allocated by single_calloc. @@ -129,7 +129,7 @@ extern void* single_calloc(void* storage, volatile uintptr_t* in_use_flag, size_ * @param in_use_flag Exact value passed to single_calloc. * @param Exact value returned by single_calloc. **/ -extern void single_free(void* storage, volatile uintptr_t* in_use_flag, void* p); +LIB_API void single_free(void* storage, volatile uintptr_t* in_use_flag, void* p); #ifdef __cplusplus @@ -219,7 +219,7 @@ void InitObject() * * raises a warning if there's not enough room (indicates incorrect usage) **/ -extern void* static_calloc(StaticStorage* ss, size_t size); +LIB_API void* static_calloc(StaticStorage* ss, size_t size); // (no need to free static_calloc-ed memory since it's in the BSS) @@ -322,7 +322,7 @@ public: { Allocs::iterator it = allocs.find(p); if(it == allocs.end()) - debug_warn("AllocatorChecker: freeing invalid pointer"); + debug_assert(0); // freeing invalid pointer else { // size must match what was passed to notify_alloc @@ -346,4 +346,13 @@ private: Allocs allocs; }; + +template +struct DummyDeleter +{ + void operator()(T* UNUSED(t)) + { + } +}; + #endif // #ifndef INCLUDED_ALLOCATORS diff --git a/source/lib/allocators/bucket.h b/source/lib/allocators/bucket.h index d243a87d80..335c39e528 100644 --- a/source/lib/allocators/bucket.h +++ b/source/lib/allocators/bucket.h @@ -59,7 +59,7 @@ struct Bucket * will be returned by bucket_alloc (whose size parameter is then ignored). * @return LibError. **/ -extern LibError bucket_create(Bucket* b, size_t el_size); +LIB_API LibError bucket_create(Bucket* b, size_t el_size); /** * free all memory that ensued from . @@ -68,7 +68,7 @@ extern LibError bucket_create(Bucket* b, size_t el_size); * * @param Bucket* **/ -extern void bucket_destroy(Bucket* b); +LIB_API void bucket_destroy(Bucket* b); /** * Dole out memory from the Bucket. @@ -79,7 +79,7 @@ extern void bucket_destroy(Bucket* b); * @return allocated memory, or 0 if the Bucket would have to be expanded and * there isn't enough memory to do so. **/ -extern void* bucket_alloc(Bucket* b, size_t size); +LIB_API void* bucket_alloc(Bucket* b, size_t size); /** * make an entry available for reuse in the given Bucket. @@ -91,6 +91,6 @@ extern void* bucket_alloc(Bucket* b, size_t size); * @param Bucket* * @param el entry allocated via bucket_alloc. **/ -extern void bucket_free(Bucket* b, void* el); +LIB_API void bucket_free(Bucket* b, void* el); #endif // #ifndef INCLUDED_BUCKET diff --git a/source/lib/allocators/dynarray.h b/source/lib/allocators/dynarray.h index 070062ea89..d124b540cd 100644 --- a/source/lib/allocators/dynarray.h +++ b/source/lib/allocators/dynarray.h @@ -45,7 +45,7 @@ struct DynArray * (* rounded up to next page size multiple) * @return LibError. **/ -extern LibError da_alloc(DynArray* da, size_t max_size); +LIB_API LibError da_alloc(DynArray* da, size_t max_size); /** * free all memory (address space + physical) that constitutes the @@ -56,7 +56,7 @@ extern LibError da_alloc(DynArray* da, size_t max_size); * @param DynArray* da; zeroed afterwards. * @return LibError **/ -extern LibError da_free(DynArray* da); +LIB_API LibError da_free(DynArray* da); /** * expand or shrink the array: changes the amount of currently committed @@ -67,7 +67,7 @@ extern LibError da_free(DynArray* da); * pages are added/removed until this is met. * @return LibError. **/ -extern LibError da_set_size(DynArray* da, size_t new_size); +LIB_API LibError da_set_size(DynArray* da, size_t new_size); /** * Make sure at least bytes starting at da->pos are committed and @@ -77,7 +77,7 @@ extern LibError da_set_size(DynArray* da, size_t new_size); * @param size Minimum amount to guarantee [bytes] * @return LibError **/ -extern LibError da_reserve(DynArray* da, size_t size); +LIB_API LibError da_reserve(DynArray* da, size_t size); /** * change access rights of the array memory. @@ -89,7 +89,7 @@ extern LibError da_reserve(DynArray* da, size_t size); * @param prot a combination of the PROT_* values used with mprotect. * @return LibError. **/ -extern LibError da_set_prot(DynArray* da, int prot); +LIB_API LibError da_set_prot(DynArray* da, int prot); /** * "wrap" (i.e. store information about) the given buffer in a DynArray. @@ -104,7 +104,7 @@ extern LibError da_set_prot(DynArray* da, int prot); * @param size maximum size (no alignment requirements) * @return LibError. **/ -extern LibError da_wrap_fixed(DynArray* da, u8* p, size_t size); +LIB_API LibError da_wrap_fixed(DynArray* da, u8* p, size_t size); /** * "read" from array, i.e. copy into the given buffer. @@ -116,7 +116,7 @@ extern LibError da_wrap_fixed(DynArray* da, u8* p, size_t size); * @param size [bytes] to copy * @return LibError. **/ -extern LibError da_read(DynArray* da, void* data_dst, size_t size); +LIB_API LibError da_read(DynArray* da, void* data_dst, size_t size); /** * "write" to array, i.e. copy from the given buffer. @@ -128,6 +128,6 @@ extern LibError da_read(DynArray* da, void* data_dst, size_t size); * @param size [bytes] to copy * @return LibError. **/ -extern LibError da_append(DynArray* da, const void* data_src, size_t size); +LIB_API LibError da_append(DynArray* da, const void* data_src, size_t size); #endif // #ifndef INCLUDED_DYNARRAY diff --git a/source/lib/allocators/headerless.cpp b/source/lib/allocators/headerless.cpp index a01aae494b..d67a38a701 100644 --- a/source/lib/allocators/headerless.cpp +++ b/source/lib/allocators/headerless.cpp @@ -730,20 +730,20 @@ HeaderlessAllocator::HeaderlessAllocator(size_t poolSize) void HeaderlessAllocator::Reset() { - return impl.get()->Reset(); + return impl->Reset(); } void* HeaderlessAllocator::Allocate(size_t size) throw() { - return impl.get()->Allocate(size); + return impl->Allocate(size); } void HeaderlessAllocator::Deallocate(void* p, size_t size) { - return impl.get()->Deallocate((u8*)p, size); + return impl->Deallocate((u8*)p, size); } void HeaderlessAllocator::Validate() const { - return impl.get()->Validate(); + return impl->Validate(); } diff --git a/source/lib/allocators/headerless.h b/source/lib/allocators/headerless.h index a7c5d667b4..a4d41ae1dc 100644 --- a/source/lib/allocators/headerless.h +++ b/source/lib/allocators/headerless.h @@ -66,7 +66,7 @@ public: private: class Impl; - boost::shared_ptr impl; + shared_ptr impl; }; #endif // #ifndef INCLUDED_HEADERLESS diff --git a/source/lib/allocators/mem_util.h b/source/lib/allocators/mem_util.h index 0d9c173c8d..523d26130a 100644 --- a/source/lib/allocators/mem_util.h +++ b/source/lib/allocators/mem_util.h @@ -38,7 +38,7 @@ extern LibError mem_Protect(u8* p, size_t size, int prot); // rationale for the function-based interface: a class encapsulating the // freelist pointer would force each header to include mem_util.h; // instead, implementations need only declare a void* pointer. -void mem_freelist_AddToFront(void*& freelist, void* el); -void* mem_freelist_Detach(void*& freelist); +extern void mem_freelist_AddToFront(void*& freelist, void* el); +extern void* mem_freelist_Detach(void*& freelist); #endif // #ifndef INCLUDED_MEM_UTIL diff --git a/source/lib/allocators/pool.cpp b/source/lib/allocators/pool.cpp index 53b51732db..eb0efa31fc 100644 --- a/source/lib/allocators/pool.cpp +++ b/source/lib/allocators/pool.cpp @@ -83,14 +83,14 @@ void pool_free(Pool* p, void* el) // check if requested_size matches that when allocating) if(p->el_size == 0) { - debug_warn("cannot free variable-size items"); + debug_assert(0); // cannot free variable-size items return; } if(pool_contains(p, el)) mem_freelist_AddToFront(p->freelist, el); else - debug_warn("invalid pointer (not in pool)"); + debug_assert(0); // invalid pointer (not in pool) } diff --git a/source/lib/allocators/pool.h b/source/lib/allocators/pool.h index 9ed44e4d52..4dad601910 100644 --- a/source/lib/allocators/pool.h +++ b/source/lib/allocators/pool.h @@ -57,7 +57,7 @@ const size_t POOL_VARIABLE_ALLOCS = ~0u; * allow variable-sized allocations, but pool_free is then unusable. * @return LibError **/ -extern LibError pool_create(Pool* p, size_t max_size, size_t el_size); +LIB_API LibError pool_create(Pool* p, size_t max_size, size_t el_size); /** * free all memory (address space + physical) that constitutes the @@ -71,7 +71,7 @@ extern LibError pool_create(Pool* p, size_t max_size, size_t el_size); * @param Pool* * @return LibError. **/ -extern LibError pool_destroy(Pool* p); +LIB_API LibError pool_destroy(Pool* p); /** * indicate whether a pointer was allocated from the given pool. @@ -81,7 +81,7 @@ extern LibError pool_destroy(Pool* p); * @param Pool* * @return bool. **/ -extern bool pool_contains(const Pool* p, void* el); +LIB_API bool pool_contains(const Pool* p, void* el); /** * Dole out memory from the pool. @@ -92,7 +92,7 @@ extern bool pool_contains(const Pool* p, void* el); * @return allocated memory, or 0 if the Pool would have to be expanded and * there isn't enough memory to do so. **/ -extern void* pool_alloc(Pool* p, size_t size); +LIB_API void* pool_alloc(Pool* p, size_t size); /** * Make a fixed-size element available for reuse in the given Pool. @@ -104,7 +104,7 @@ extern void* pool_alloc(Pool* p, size_t size); * @param Pool* * @param el Element returned by pool_alloc. **/ -extern void pool_free(Pool* p, void* el); +LIB_API void pool_free(Pool* p, void* el); /** * "free" all user allocations that ensued from the given Pool. @@ -114,7 +114,7 @@ extern void pool_free(Pool* p, void* el); * * @param Pool* **/ -extern void pool_free_all(Pool* p); +LIB_API void pool_free_all(Pool* p); #ifdef __cplusplus diff --git a/source/lib/bits.cpp b/source/lib/bits.cpp index df904c1c0c..47394421e3 100644 --- a/source/lib/bits.cpp +++ b/source/lib/bits.cpp @@ -10,7 +10,7 @@ #include "precompiled.h" -#if CPU_IA32 +#if ARCH_IA32 # include "lib/sysdep/ia32/ia32_asm.h" // ia32_asm_log2_of_pow2 #endif @@ -28,7 +28,7 @@ int log2_of_pow2(uint n) { int bit_index; -#if CPU_IA32 +#if ARCH_IA32 bit_index = ia32_asm_log2_of_pow2(n); #else if(!is_pow2(n)) diff --git a/source/lib/bits.h b/source/lib/bits.h index 7fdcde3cf6..6ecc446e36 100644 --- a/source/lib/bits.h +++ b/source/lib/bits.h @@ -113,4 +113,11 @@ extern uint round_up_to_pow2(uint x); extern uintptr_t round_up (uintptr_t n, uintptr_t multiple); extern uintptr_t round_down(uintptr_t n, uintptr_t multiple); +template +bool IsAligned(T t, uintptr_t multiple) +{ + return ((uintptr_t)t % multiple) == 0; + +} + #endif // #ifndef INCLUDED_BITS diff --git a/source/lib/byte_order.h b/source/lib/byte_order.h index 07fc644658..849e1be9f1 100644 --- a/source/lib/byte_order.h +++ b/source/lib/byte_order.h @@ -11,7 +11,18 @@ #ifndef INCLUDED_BYTE_ORDER #define INCLUDED_BYTE_ORDER -#include "config.h" +#include "lib/sysdep/cpu.h" + +// detect byte order via predefined macros. +#ifndef BYTE_ORDER +# define LITTLE_ENDIAN 0x4321 +# define BIG_ENDIAN 0x1234 +# if ARCH_IA32 || ARCH_IA64 || ARCH_AMD64 || ARCH_ALPHA || ARCH_ARM || ARCH_MIPS || defined(__LITTLE_ENDIAN__) +# define BYTE_ORDER LITTLE_ENDIAN +# else +# define BYTE_ORDER BIG_ENDIAN +# endif +#endif /** diff --git a/source/lib/cache_adt.h b/source/lib/cache_adt.h index 1c64294dcf..2ecb24fc31 100644 --- a/source/lib/cache_adt.h +++ b/source/lib/cache_adt.h @@ -233,8 +233,11 @@ public: void remove(Key key) { MapIt it = map.find(key); - debug_assert(it != map.end()); - remove_(it); + // note: don't complain if not in the cache: this happens after + // writing a file and invalidating its cache entry, which may + // or may not exist. + if(it != map.end()) + remove_(it); } void on_access(Entry& entry) @@ -287,9 +290,10 @@ again: } protected: - // note: use hash_map instead of map for better locality - // (relevant when iterating over all items in remove_least_valuable) - class Map : public STL_HASH_MAP + // note: hash_map is probably better in terms of locality + // (relevant when iterating over all items in remove_least_valuable), + // but would require a hash comparator for VfsPath. + class Map : public std::map { public: static Entry& entry_from_it(typename Map::iterator it) { return it->second; } @@ -557,7 +561,7 @@ public: return; } } - debug_warn("entry not found in list"); + debug_assert(0); // entry not found in list } void remove_least_valuable(std::list& entry_list) diff --git a/source/lib/code_annotation.h b/source/lib/code_annotation.h index 3050057f06..12078d83e2 100644 --- a/source/lib/code_annotation.h +++ b/source/lib/code_annotation.h @@ -74,7 +74,7 @@ checking, but does not cause any compiler warnings. # if !MSC_VERSION || CONFIG_PARANOIA # define UNREACHABLE\ STMT(\ - debug_warn("hit supposedly unreachable code");\ + debug_assert(0); /* hit supposedly unreachable code */\ abort();\ ) // b) VC only: don't generate any code; squelch the warning and optimize. @@ -125,15 +125,17 @@ switch(x % 2) * no runtime overhead; may be used anywhere, including file scope. * especially useful for testing sizeof types. * - * this version has a more descriptive error message, but may cause a - * struct redefinition warning if used from the same line in different files. - * - * note: alternative method in C++: specialize a struct only for true; - * using it will raise 'incomplete type' errors if instantiated with false. - * * @param expression that is expected to evaluate to non-zero at compile-time. **/ -#define cassert(expr) struct UID__ { unsigned int CASSERT_FAILURE: (expr); } +#define cassert(expr) typedef detail::static_assert<(expr)>::type UID__; +namespace detail +{ + template struct static_assert; + template<> struct static_assert + { + typedef int type; + }; +} /** * compile-time debug_assert. causes a compile error if the expression diff --git a/source/lib/config.h b/source/lib/config.h index eb01c64354..e1dcce0b70 100644 --- a/source/lib/config.h +++ b/source/lib/config.h @@ -2,7 +2,7 @@ * ========================================================================= * File : config.h * Project : 0 A.D. - * Description : compile-time configuration and capability detection. + * Description : user-specified compile-time configuration. * ========================================================================= */ @@ -11,18 +11,13 @@ #ifndef INCLUDED_CONFIG #define INCLUDED_CONFIG -// the config/have macros are always defined; their values (1 or 0) are +// the config macros are always defined; their values (1 or 0) are // tested with #if instead of #ifdef. // this protects user code from typos such as #ifdef _MSC_VEER, which // would silently remove code. instead, we will at least get "test of // undefined macro" warnings here. not including this header also triggers // such warnings, but won't happen often because we're included from PCH. - - -//----------------------------------------------------------------------------- -// user-specified configuration choices -//----------------------------------------------------------------------------- - +// // allow override via compiler settings by checking #ifndef. // omit frame pointers - stack frames will not be generated, which @@ -36,7 +31,7 @@ # endif #endif -// (only applicable if CPU_IA32) 64-bit values will be returned in EDX:EAX. +// (only applicable if ARCH_IA32) 64-bit values will be returned in EDX:EAX. // this chiefly affects ia32_rdtsc. if not set, a safer but slower fallback // will be used that doesn't assume anything about return convention. #ifndef CONFIG_RETURN64_EDX_EAX @@ -46,11 +41,7 @@ // allow use of RDTSC for raw tick counts (otherwise, the slower but // more reliable on MP systems wall-clock will be used). #ifndef CONFIG_TIMER_ALLOW_RDTSC -# if CPU_IA32 -# define CONFIG_TIMER_ALLOW_RDTSC 1 -# else -# define CONFIG_TIMER_ALLOW_RDTSC 0 -# endif +# define CONFIG_TIMER_ALLOW_RDTSC 1 #endif // this enables/disables the actual checking done by OverrunProtector-s @@ -81,12 +72,11 @@ # define CONFIG_PARANOIA 0 #endif -// finale release; disables some safety checks. +// final release; disables some safety checks. #ifndef CONFIG_FINAL # define CONFIG_FINAL 0 #endif - // enable trace output for low-level code - various functions will // debug_printf when they are entered/exited. note that the appropriate // TRACEn tags must be debug_filter_add-ed for this to have any effect. @@ -100,341 +90,9 @@ # define CONFIG_DISABLE_EXCEPTIONS 0 #endif - -//----------------------------------------------------------------------------- -// auto-detect OS and platform via predefined macros -//----------------------------------------------------------------------------- - -// rationale: -// - these macros have consistent names and numerical values; using -// them saves other code from having to know the obscure predefined macros. -// - we'd like to use #if/#elif/#endif chains for e.g. OS_* to allow warning -// if none is selected, but there's no good way to #define all inapplicable -// settings to 0. doing so up front is hard to maintain and would require -// #undef before setting any one to 1. #ifndef afterwards for each setting -// is ugly and brittle as well. we therefore use #if/#else/#endif. - -// compiler -// 0 if not present, or (major*100 + minor). note that more than -// one *_VERSION may be non-zero due to interoperability (e.g. ICC with MSC). -// .. VC -#ifdef _MSC_VER -# define MSC_VERSION _MSC_VER -#else -# define MSC_VERSION 0 -#endif -// .. ICC (VC-compatible) -#if defined(__INTEL_COMPILER) -# define ICC_VERSION __INTEL_COMPILER -#else -# define ICC_VERSION 0 -#endif -// .. LCC (VC-compatible) -#if defined(__LCC__) -# define LCC_VERSION __LCC__ -#else -# define LCC_VERSION 0 -#endif -// .. GCC -#ifdef __GNUC__ -# define GCC_VERSION (__GNUC__*100 + __GNUC_MINOR__) -#else -# define GCC_VERSION 0 -#endif - - -// STL -// (checked by STL implementation-specific code in debug_stl). -// .. Dinkumware -#if MSC_VERSION -# include // defines _CPPLIB_VER -#endif -#if defined(_CPPLIB_VER) -# define STL_DINKUMWARE _CPPLIB_VER -#else -# define STL_DINKUMWARE 0 -#endif -// .. GCC -#if defined(__GLIBCPP__) -# define STL_GCC __GLIBCPP__ -#elif defined(__GLIBCXX__) -# define STL_GCC __GLIBCXX__ -#else -# define STL_GCC 0 -#endif -// .. ICC -#if defined(__INTEL_CXXLIB_ICC) -# define STL_ICC __INTEL_CXXLIB_ICC -#else -# define STL_ICC 0 -#endif - - -// OS -// .. Windows -#if defined(_WIN64) -# define OS_WIN64 1 -#else -# define OS_WIN64 0 -#endif -#if defined(_WIN32) -# define OS_WIN 1 -#else -# define OS_WIN 0 -#endif -// .. Linux -#if defined(linux) || defined(__linux) || defined(__linux__) -# define OS_LINUX 1 -#else -# define OS_LINUX 0 -#endif -// .. Mac OS X -#if (defined(__APPLE__) && defined(__MACH__)) -# define OS_MACOSX 1 -#else -# define OS_MACOSX 0 -#endif -// .. BSD -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) -# define OS_BSD 1 -#else -# define OS_BSD 0 -#endif -// .. Solaris -#if defined(sun) || defined(__sun) -# define OS_SOLARIS 1 -#else -# define OS_SOLARIS 0 -#endif -// .. BeOS -#if defined(__BEOS__) -# define OS_BEOS 1 -#else -# define OS_BEOS 0 -#endif -// .. Mac OS 9 or below -#if defined(macintosh) -# define OS_MAC 1 -#else -# define OS_MAC 0 -#endif -// .. Amiga -#if defined(AMIGA) -# define OS_AMIGA 1 -#else -# define OS_AMIGA 0 -#endif -// .. Unix-based -#if defined(unix) || defined(__unix) || defined(__unix__) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) -# define OS_UNIX 1 -#else -# define OS_UNIX 0 -#endif - -// convenience: additionally set OS_UNIX for Unix-based OSes -// note: doing this in an separate section instead of adding the extra define -// to all affected OSes saves a few undefs or macro redefinition warnings. -#if OS_LINUX || OS_MACOSX || OS_BSD || OS_SOLARIS -# undef OS_UNIX -# define OS_UNIX 1 -#endif - -// convenience: additionally set OS_BSD for BSD-based OSes. see note above. -#if OS_MACOSX -# undef OS_BSD -# define OS_BSD 1 -#endif - - -// CPU -// .. IA-32 -#if defined(_M_IX86) || defined(i386) || defined(_X86_) -# define CPU_IA32 1 -#else -# define CPU_IA32 0 -#endif -// .. IA-64 -#if defined(_M_IA64) || defined(__ia64__) -# define CPU_IA64 1 -#else -# define CPU_IA64 0 -#endif -// .. AMD64 -#if defined(_M_AMD64) || defined(__amd64__) || defined(__amd64) -# define CPU_AMD64 1 -#else -# define CPU_AMD64 0 -#endif -// .. Alpha -#if defined(_M_ALPHA) || defined(__alpha__) || defined(__alpha) -# define CPU_ALPHA 1 -#else -# define CPU_ALPHA 0 -#endif -// .. ARM -#if defined(__arm__) -# define CPU_ARM 1 -#else -# define CPU_ARM 0 -#endif -// .. MIPS -#if defined(__MIPS__) || defined(__mips__) || defined(__mips) -# define CPU_MIPS 1 -#else -# define CPU_MIPS 0 -#endif - - -// byte order -// if already defined by someone else, assume they are also correct :P -#ifndef BYTE_ORDER -# define LITTLE_ENDIAN 0x4321 -# define BIG_ENDIAN 0x1234 -# if CPU_IA32 || CPU_IA64 || CPU_AMD64 || CPU_ALPHA || CPU_ARM || CPU_MIPS || defined(__LITTLE_ENDIAN__) -# define BYTE_ORDER LITTLE_ENDIAN -# else -# define BYTE_ORDER BIG_ENDIAN -# endif -#endif - - -//----------------------------------------------------------------------------- -// auto-detect platform features, given the above information -//----------------------------------------------------------------------------- - -// check if compiling in pure C mode (not C++) with support for C99. -// (this is more convenient than testing __STDC_VERSION__ directly) -// -// note: C99 provides several useful but disjunct bits of functionality. -// unfortunately, most C++ compilers do not offer a complete implementation. -// however, many of these features are likely to be added to C++, and/or are -// already available as extensions. what we'll do is add a HAVE_ macro for -// each feature and test those instead. they are set if HAVE_C99, or also if -// the compiler happens to support something compatible. -// -// rationale: lying about __STDC_VERSION__ via Premake so as to enable support -// for some C99 functions doesn't work. Mac OS X headers would then use the -// restrict keyword, which is never supported by g++ (because that might -// end up breaking valid C++98 programs). -#define HAVE_C99 0 -#ifdef __STDC_VERSION__ -# if __STDC_VERSION__ >= 199901L -# undef HAVE_C99 -# define HAVE_C99 1 -# endif -#endif - -// strdup, wcsdup -#if OS_MACOSX -# define HAVE_STRDUP 1 -# define HAVE_WCSDUP 0 -#else -# define HAVE_STRDUP 1 -# define HAVE_WCSDUP 1 -#endif - -// emulation needed on VC8 because this function is "deprecated". -// not present on VC7, either. -#if MSC_VERSION -# define HAVE_MKDIR 0 -#else -# define HAVE_MKDIR 1 -#endif - -// rint*, fminf, fpclassify (too few/diverse to make separate HAVE_ for each) -#if HAVE_C99 || GCC_VERSION -# define HAVE_C99_MATH 1 -#else -# define HAVE_C99_MATH 0 -#endif - -// gettimeofday() -#if OS_UNIX -# define HAVE_GETTIMEOFDAY 1 -#else -# define HAVE_GETTIMEOFDAY 0 -#endif - -// clock_gettime() -#define HAVE_CLOCK_GETTIME 0 -#if OS_UNIX -# include -# if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 -# undef HAVE_CLOCK_GETTIME -# define HAVE_CLOCK_GETTIME 1 -# endif -#endif - -// X server -#if OS_LINUX -# define HAVE_X 1 -#else -# define HAVE_X 0 -#endif - -// MSVC/ICC-style __asm{} blocks (Intel syntax) -#if MSC_VERSION -# define HAVE_MS_ASM 1 -#else -# define HAVE_MS_ASM 0 -#endif - -// GNU-style __asm() blocks (AT&T syntax) -#if GCC_VERSION -# define HAVE_GNU_ASM 1 -#else -# define HAVE_GNU_ASM 0 -#endif - // precompiled headers (affects what precompiled.h pulls in; see there) -#if MSC_VERSION -# define HAVE_PCH 1 -#elif defined(USING_PCH) -# define HAVE_PCH 1 -#else -# define HAVE_PCH 0 -#endif - -// VC debug memory allocator / leak detector -// notes: -// - PCH is required because it makes sure system headers are included -// before redefining new (otherwise, tons of errors result); -// - disabled on ICC9 because the ICC 9.0.006 beta appears to generate -// incorrect code when we redefine new. -// TODO: remove when no longer necessary -#if MSC_VERSION && \ - (!defined(NDEBUG) || defined(TESTING)) && \ - HAVE_PCH && \ - ICC_VERSION != 900 -# define HAVE_VC_DEBUG_ALLOC 1 -#else -# define HAVE_VC_DEBUG_ALLOC 0 -#endif - -// nonstandard STL containers -#define HAVE_STL_SLIST 0 -#if STL_DINKUMWARE -# define HAVE_STL_HASH 1 -#else -# define HAVE_STL_HASH 0 -#endif - -// safe CRT functions: strcpy_s, fopen_s, etc. -// these are always available to users: if not provided by the CRT, we -// implement them ourselves. this flag is only used to skip our impl. -#if MSC_VERSION >= 1400 -# define HAVE_SECURE_CRT 1 -#else -# define HAVE_SECURE_CRT 0 -#endif - - -// should we use our float->int code? newer GCC versions with -ffast-math and -// VC8 can use SSE, so skip it there. -#if CPU_IA32 && MSC_VERSION && MSC_VERSION < 1400 -# define USE_IA32_FLOAT_TO_INT 1 -#else -# define USE_IA32_FLOAT_TO_INT 0 +#ifndef CONFIG_ENABLE_PCH +# define CONFIG_ENABLE_PCH 1 #endif #endif // #ifndef INCLUDED_CONFIG diff --git a/source/lib/debug.cpp b/source/lib/debug.cpp index 02f2f5ce23..e48613163a 100644 --- a/source/lib/debug.cpp +++ b/source/lib/debug.cpp @@ -70,7 +70,7 @@ void debug_wprintf_mem(const wchar_t* fmt, ...) va_end(args); if(len < 0) { - debug_warn("vswprintf failed"); + debug_assert(0); // vswprintf failed return; } debug_log_pos += len+2; @@ -112,7 +112,7 @@ void debug_filter_add(const char* tag) // too many already? if(num_tags == MAX_TAGS) { - debug_warn("increase MAX_TAGS"); + debug_assert(0); // increase MAX_TAGS return; } @@ -204,14 +204,11 @@ void debug_wprintf(const wchar_t* fmt, ...) const size_t MAX_BYTES = MAX_CHARS*2; char mbs_buf[MAX_BYTES]; mbs_buf[MAX_BYTES-1] = '\0'; size_t bytes_written = wcstombs(mbs_buf, wcs_buf, MAX_BYTES); - // .. error - if(bytes_written == (size_t)-1) - debug_warn("invalid wcs character encountered"); - // .. exact fit, make sure it's 0-terminated + debug_assert(bytes_written != (size_t)-1); // else: invalid wcs character encountered + debug_assert(bytes_written <= MAX_BYTES); // else: overflow (should be impossible) + // exact fit, ensure it's 0-terminated if(bytes_written == MAX_BYTES) mbs_buf[MAX_BYTES-1] = '\0'; - // .. paranoia: overflow is impossible - debug_assert(bytes_written <= MAX_BYTES); if(debug_filter_allows(mbs_buf)) debug_puts(mbs_buf); @@ -396,8 +393,7 @@ static void symbol_string_add_to_cache(const char* string, void* symbol) { // note: must be zeroed to set each Symbol to "invalid" symbols = (Symbol*)calloc(MAX_SYMBOLS, sizeof(Symbol)); - if(!symbols) - debug_warn("failed to allocate symbols"); + debug_assert(symbols); } // hash table is completely full (guard against infinite loop below). @@ -680,7 +676,7 @@ void debug_skip_next_err(LibError err) if(cpu_CAS(&expected_err_valid, 0, 1)) expected_err = err; else - debug_warn("internal error: concurrent attempt to skip assert/error"); + debug_assert(0); // internal error: concurrent attempt to skip assert/error } @@ -691,8 +687,7 @@ static bool should_skip_this_error(LibError err) // (use cpu_CAS to ensure only one error is skipped) if(cpu_CAS(&expected_err_valid, 1, 0)) { - if(!was_expected_err) - debug_warn("anticipated error was not raised"); + debug_assert(was_expected_err); return was_expected_err; } diff --git a/source/lib/debug.h b/source/lib/debug.h index 566e7d3ca3..cbb97e51d6 100644 --- a/source/lib/debug.h +++ b/source/lib/debug.h @@ -65,7 +65,7 @@ even if debug information is present and assert dialogs are useless. * check heap integrity (independently of mmgr). * errors are reported by the CRT or via debug_display_error. **/ -extern void debug_heap_check(void); +LIB_API void debug_heap_check(void); enum DebugHeapChecks { @@ -90,7 +90,7 @@ enum DebugHeapChecks * call at any time; from then on, the specified checks will be performed. * if not called, the default is DEBUG_HEAP_NONE, i.e. do nothing. **/ -extern void debug_heap_enable(DebugHeapChecks what); +LIB_API void debug_heap_enable(DebugHeapChecks what); //----------------------------------------------------------------------------- @@ -102,7 +102,7 @@ enum DebugLevel DEBUG_LEVEL_NONE = 0, DEBUG_LEVEL_BRIEF = 2, DEBUG_LEVEL_DETAILED = 5, - DEBUG_LEVEL_FULL = 9, + DEBUG_LEVEL_FULL = 9 }; #define DEBUG_PRINTF_BRIEF if(debug_level >= DEBUG_LEVEL_BRIEF) debug_printf #define DEBUG_PRINTF_DETAILED if(debug_level >= DEBUG_LEVEL_DETAILED) debug_printf @@ -114,10 +114,10 @@ enum DebugLevel * * @param format string and varargs; see printf. **/ -extern void debug_printf(const char* fmt, ...); +LIB_API void debug_printf(const char* fmt, ...); /// note: this merely converts to a MBS and calls debug_printf. -extern void debug_wprintf(const wchar_t* fmt, ...); +LIB_API void debug_wprintf(const wchar_t* fmt, ...); /** @@ -126,7 +126,7 @@ extern void debug_wprintf(const wchar_t* fmt, ...); * is unavailable because that function is much more capable. * implemented via sys_display_msgw; see documentation there. **/ -extern void debug_display_msgw(const wchar_t* caption, const wchar_t* msg); +LIB_API void debug_display_msgw(const wchar_t* caption, const wchar_t* msg); /// flags to customize debug_display_error behavior enum DebugDisplayErrorFlags @@ -219,7 +219,7 @@ enum ErrorReaction * provides the storage. values: see DEBUG_SUPPRESS above. * @return ErrorReaction (user's choice: continue running or stop?) **/ -extern ErrorReaction debug_display_error(const wchar_t* description, +LIB_API ErrorReaction debug_display_error(const wchar_t* description, uint flags, uint skip, void* context, const char* file, int line, const char* func, u8* suppress); @@ -258,26 +258,26 @@ extern ErrorReaction debug_display_error(const wchar_t* description, * in future, allow output with the given tag to proceed. * no effect if already added. **/ -extern void debug_filter_add(const char* tag); +LIB_API void debug_filter_add(const char* tag); /** * in future, discard output with the given tag. * no effect if not currently added. **/ -extern void debug_filter_remove(const char* tag); +LIB_API void debug_filter_remove(const char* tag); /** * clear all filter state; equivalent to debug_filter_remove for * each tag that was debug_filter_add-ed. **/ -extern void debug_filter_clear(); +LIB_API void debug_filter_clear(); /** * indicate if the given text would be printed. * useful for a series of debug_printfs - avoids needing to add a tag to * each of their format strings. **/ -extern bool debug_filter_allows(const char* text); +LIB_API bool debug_filter_allows(const char* text); /** @@ -286,7 +286,7 @@ extern bool debug_filter_allows(const char* text); * * @param format string and varags; see printf. **/ -extern void debug_wprintf_mem(const wchar_t* fmt, ...); +LIB_API void debug_wprintf_mem(const wchar_t* fmt, ...); /** * write an error description and all logs into crashlog.txt @@ -298,7 +298,7 @@ extern void debug_wprintf_mem(const wchar_t* fmt, ...); * @return LibError; ERR::REENTERED if reentered via recursion or * multithreading (not allowed since an infinite loop may result). **/ -extern LibError debug_write_crashlog(const wchar_t* text); +LIB_API LibError debug_write_crashlog(const wchar_t* text); //----------------------------------------------------------------------------- @@ -343,7 +343,7 @@ STMT(\ * show a dialog to make sure unexpected states in the program are noticed. * this is less error-prone than "debug_assert(0 && "text");" and avoids * "conditional expression is constant" warnings. we'd really like to - * completely eliminate the problem; replacing 0 literals with extern + * completely eliminate the problem; replacing 0 literals with LIB_API * volatile variables fools VC7 but isn't guaranteed to be free of overhead. * we therefore just squelch the warning (unfortunately non-portable). * this duplicates the code from debug_assert to avoid compiler warnings about @@ -393,7 +393,7 @@ STMT(\ * @param func name of the function containing it * @return ErrorReaction (user's choice: continue running or stop?) **/ -extern ErrorReaction debug_assert_failed(const char* assert_expr, +LIB_API ErrorReaction debug_assert_failed(const char* assert_expr, u8* suppress, const char* file, int line, const char* func); @@ -407,7 +407,7 @@ extern ErrorReaction debug_assert_failed(const char* assert_expr, * @param func name of the function containing it * @return ErrorReaction (user's choice: continue running or stop?) **/ -extern ErrorReaction debug_warn_err(LibError err, +LIB_API ErrorReaction debug_warn_err(LibError err, u8* suppress, const char* file, int line, const char* func); @@ -428,14 +428,14 @@ extern ErrorReaction debug_warn_err(LibError err, * note: this is thread-safe, but to prevent confusion, only one * concurrent skip request is allowed. */ -extern void debug_skip_next_err(LibError err); +LIB_API void debug_skip_next_err(LibError err); /** * same as debug_skip_next_err, but for asserts. * note that this is implemented in terms of it, so only one assert or * error skip request may be active at a time. */ -extern void debug_skip_assert(); +LIB_API void debug_skip_assert(); //----------------------------------------------------------------------------- @@ -482,13 +482,13 @@ enum DbgBreakType * @return LibError; ERR::LIMIT if no more breakpoints are available * (they are a limited resource - only 4 on IA-32). **/ -extern LibError debug_set_break(void* addr, DbgBreakType type); +LIB_API LibError debug_set_break(void* addr, DbgBreakType type); /** * remove all breakpoints that were set by debug_set_break. * important, since these are a limited resource. **/ -extern LibError debug_remove_all_breaks(); +LIB_API LibError debug_remove_all_breaks(); //----------------------------------------------------------------------------- @@ -545,7 +545,7 @@ const size_t DBG_FILE_LEN = 100; * @return LibError; INFO::OK iff any information was successfully * retrieved and stored. **/ -extern LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line); +LIB_API LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line); /** * write a complete stack trace (including values of local variables) into @@ -562,9 +562,9 @@ extern LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char * @return LibError; ERR::REENTERED if reentered via recursion or * multithreading (not allowed since static data is used). **/ -extern LibError debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void* context); +LIB_API LibError debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void* context); -extern const char* debug_get_symbol_string(void* symbol, const char* name, const char* file, int line); +LIB_API const char* debug_get_symbol_string(void* symbol, const char* name, const char* file, int line); //----------------------------------------------------------------------------- @@ -576,7 +576,7 @@ extern const char* debug_get_symbol_string(void* symbol, const char* name, const * this can be quite slow (~1 ms)! On Windows, it uses OutputDebugString * (entails context switch), otherwise stdout+fflush (waits for IO). **/ -extern void debug_puts(const char* text); +LIB_API void debug_puts(const char* text); /** * return address of the Nth function on the call stack. @@ -590,7 +590,7 @@ extern void debug_puts(const char* text); * for exceptions. otherwise, tracing starts from the current call stack. * @return address of Nth function **/ -extern void* debug_get_nth_caller(uint skip, void* context); +LIB_API void* debug_get_nth_caller(uint skip, void* context); /** * check if a pointer appears to be totally invalid. @@ -601,13 +601,13 @@ extern void* debug_get_nth_caller(uint skip, void* context); * @param p pointer * @return 1 if totally bogus, otherwise 0. **/ -extern int debug_is_pointer_bogus(const void* p); +LIB_API int debug_is_pointer_bogus(const void* p); /// does the given pointer appear to point to code? -extern bool debug_is_code_ptr(void* p); +LIB_API bool debug_is_code_ptr(void* p); /// does the given pointer appear to point to the stack? -extern bool debug_is_stack_ptr(void* p); +LIB_API bool debug_is_stack_ptr(void* p); /** @@ -621,7 +621,7 @@ extern bool debug_is_stack_ptr(void* p); * the entire program; best to pass a string literal. allocating a copy * would be quite a bit more work due to cleanup issues. **/ -extern void debug_set_thread_name(const char* name); +LIB_API void debug_set_thread_name(const char* name); /** * return current thread's name. @@ -629,7 +629,7 @@ extern void debug_set_thread_name(const char* name); * @return thread name, or NULL if one hasn't been assigned yet * via debug_set_thread_name. **/ -extern const char* debug_get_thread_name(); +LIB_API const char* debug_get_thread_name(); /** @@ -654,7 +654,7 @@ struct ErrorMessageMem * * @param ErrorMessageMem* **/ -extern void debug_error_message_free(ErrorMessageMem* emm); +LIB_API void debug_error_message_free(ErrorMessageMem* emm); /** * build a string describing the given error. @@ -671,7 +671,7 @@ extern void debug_error_message_free(ErrorMessageMem* emm); * fallback in case heap alloc fails. should be freed via * debug_error_message_free when no longer needed. **/ -extern const wchar_t* debug_error_message_build( +LIB_API const wchar_t* debug_error_message_build( const wchar_t* description, const char* fn_only, int line, const char* func, uint skip, void* context, @@ -682,6 +682,6 @@ extern const wchar_t* debug_error_message_build( * call at exit to avoid some leaks. * not strictly necessary. **/ -extern void debug_shutdown(); +LIB_API void debug_shutdown(); #endif // #ifndef INCLUDED_DEBUG diff --git a/source/lib/frequency_filter.cpp b/source/lib/frequency_filter.cpp new file mode 100644 index 0000000000..9d9b323845 --- /dev/null +++ b/source/lib/frequency_filter.cpp @@ -0,0 +1,216 @@ +#include "precompiled.h" +#include "frequency_filter.h" + + +/** + * variable-width window for frequency determination + **/ +class FrequencyEstimator : boost::noncopyable +{ +public: + FrequencyEstimator(double resolution) + : m_minDeltaTime(4.0 * resolution) // chosen to reduce error but still yield rapid updates. + , m_lastTime(0) // will be set on first XX call + , m_numEvents(1) + { + } + + bool operator()(double time, double& frequency) + { + // first call + if(m_lastTime == 0.0) + { + m_lastTime = time; + return false; + } + + // count # events until deltaTime is large enough + // (reduces quantization errors if resolution is low) + const double deltaTime = time - m_lastTime; + if(deltaTime < m_minDeltaTime) + { + m_numEvents++; + return false; + } + + m_lastTime = time; + frequency = (1.0 / deltaTime) * m_numEvents; + m_numEvents = 1; + return true; // success + } + +private: + const double m_minDeltaTime; + double m_lastTime; + int m_numEvents; +}; + + +/** + * variable-gain IIR filter + **/ +class IirFilter +{ +public: + IirFilter(double old) + : m_old(old) + { + } + + // bias = 0: no change. > 0: increase (n-th root). < 0: decrease (^n) + double operator()(double x, int bias) + { + // sensitivity to changes ([0,1]). + // approximately equal to a 16 sample average. + const double gain = pow(0.08, ComputeExponent(bias)); + return m_old = x*gain + m_old*(1.0-gain); + } + +private: + static double ComputeExponent(int bias) + { + if(bias > 0) + return 1.0 / bias; // n-th root + else if(bias == 0) + return 1.0; // no change + else + return -bias; // power-of-n + } + + double m_old; +}; + + +/** + * regulate IIR gain for rapid but smooth tracking of a function. + * this is similar in principle to a PID controller but is tuned for + * the special case of FPS values to simplify stabilizing the filter. + **/ +class Controller +{ +public: + Controller(double initialValue) + : m_timesOnSameSide(0) + { + std::fill(m_history, m_history+m_historySize, initialValue); + } + + // bias := exponential change to gain, (-inf, inf) + int ComputeBias(double smoothedValue, double value) + { + if(!WasOnSameSide(value)) // (must be done before updating history) + m_timesOnSameSide = 0; // see below + + // update history + std::copy(m_history, m_history+m_historySize, m_history+1); + m_history[m_historySize-1] = value; + + // suppress large jumps. + if(Change(m_history[m_historySize-1], value) > 0.30) + return -4; // gain -> 0 + + // dampen spikes/bounces. + if(WasSpike()) + return -1; + + if(Change(smoothedValue, value) > 0.02) // ignore minor jitter + { + m_timesOnSameSide++; + + // if the past few samples have been consistently above/below + // average, the function is changing and we need to catch up. + // (similar to I in a PID) + if(m_timesOnSameSide >= 3) + return std::min(m_timesOnSameSide, 4); + } + + return 0; + } + +private: + bool WasOnSameSide(double value) const + { + int sum = 0; + for(size_t i = 0; i < m_historySize; i++) + { + const int vote = (value >= m_history[i])? 1 : -1; + sum += vote; + } + return abs(sum) == m_historySize; + } + + static double Change(double from, double to) + { + return fabs(from - to) / from; + } + + // /\ or \/ in last three history entries + bool WasSpike() const + { + cassert(m_historySize >= 3); + const double h2 = m_history[m_historySize-3], h1 = m_history[m_historySize-2], h0 = m_history[m_historySize-1]; + if(((h2-h1) * (h1-h0)) > 0) // no sign change + return false; + if(Change(h2, h0) > 0.05) // overall change from oldest to newest value + return false; + if(Change(h1, h0) < 0.10) // no intervening spike + return false; + return true; + } + + static const size_t m_historySize = 3; + double m_history[m_historySize]; + int m_timesOnSameSide; +}; + + +class FrequencyFilter : public IFrequencyFilter +{ +public: + FrequencyFilter(double resolution, double expectedFrequency) + : m_controller(expectedFrequency), m_frequencyEstimator(resolution), m_iirFilter(expectedFrequency) + , m_stableFrequency(expectedFrequency), m_smoothedFrequency(expectedFrequency) + { + } + + virtual void Update(double time) + { + double frequency; + if(!m_frequencyEstimator(time, frequency)) + return; + + const int bias = m_controller.ComputeBias(m_smoothedFrequency, frequency); + m_smoothedFrequency = m_iirFilter(frequency, bias); + + // allow the smoothed FPS to free-run until it is no longer near the + // previous stable FPS value. round up because values are more often + // too low than too high. + const double difference = fabs(m_smoothedFrequency - m_stableFrequency); + if(difference > fminf(5.f, 0.05f*m_stableFrequency)) + m_stableFrequency = (int)(m_smoothedFrequency + 0.99); + } + + virtual double SmoothedFrequency() const + { + return m_smoothedFrequency; + } + + virtual int StableFrequency() const + { + return m_stableFrequency; + } + +private: + FrequencyEstimator m_frequencyEstimator; + Controller m_controller; + IirFilter m_iirFilter; + + int m_stableFrequency; + double m_smoothedFrequency; +}; + + +PIFrequencyFilter CreateFrequencyFilter(double resolution, double expectedFrequency) +{ + return PIFrequencyFilter(new FrequencyFilter(resolution, expectedFrequency)); +} diff --git a/source/lib/frequency_filter.h b/source/lib/frequency_filter.h new file mode 100644 index 0000000000..51e09d1c3e --- /dev/null +++ b/source/lib/frequency_filter.h @@ -0,0 +1,21 @@ +#ifndef INCLUDED_FREQUENCY_FILTER +#define INCLUDED_FREQUENCY_FILTER + +// calculate frequency of events (tuned for 100 Hz) +struct IFrequencyFilter +{ + virtual void Update(double value) = 0; + + // smoothed but rapidly tracked frequency + virtual double SmoothedFrequency() const = 0; + + // stable, non-fluctuating value for user display + virtual int StableFrequency() const = 0; +}; + +typedef shared_ptr PIFrequencyFilter; + +// expectedFrequency is a guess that hopefully speeds up convergence +LIB_API PIFrequencyFilter CreateFrequencyFilter(double resolution, double expectedFrequency); + +#endif // #ifndef INCLUDED_FREQUENCY_FILTER diff --git a/source/lib/input.cpp b/source/lib/input.cpp index 617cf53de3..a686115d28 100644 --- a/source/lib/input.cpp +++ b/source/lib/input.cpp @@ -47,7 +47,7 @@ static void dispatch_ev(const SDL_Event_* ev) continue; // .. invalid return value else - debug_warn("invalid handler return value"); + debug_assert(0); // invalid handler return value } } diff --git a/source/lib/lf_alloc.cpp b/source/lib/lf_alloc.cpp index 83f052f7a3..227ea6b017 100644 --- a/source/lib/lf_alloc.cpp +++ b/source/lib/lf_alloc.cpp @@ -498,7 +498,7 @@ return pool; debug_assert(0 <= num_pools && num_pools <= MAX_POOLS); if(num_pools >= MAX_POOLS) { -debug_warn("increase MAX_POOLS"); +debug_assert(0); // increase MAX_POOLS return 0; } @@ -516,7 +516,7 @@ size = round_up(size, 8); // would overflow a bucket if(size > BUCKET_SIZE-sizeof(u8*)) { -debug_warn("sbh_alloc: size doesn't fit in a bucket"); +debug_assert(0); // size doesn't fit in a bucket return 0; } @@ -533,7 +533,7 @@ TNode* node_alloc(size_t size) // would overflow a bucket if(size > BUCKET_SIZE-sizeof(u8*)) { -debug_warn("node_alloc: size doesn't fit in a bucket"); +debug_assert(0); // size doesn't fit in a bucket return 0; } diff --git a/source/lib/lib.cpp b/source/lib/lib.cpp index e0ab2da655..4258429729 100644 --- a/source/lib/lib.cpp +++ b/source/lib/lib.cpp @@ -81,7 +81,7 @@ u8 u8_from_double(double in) { if(!(0.0 <= in && in < 1.0)) { - debug_warn("clampf not in [0,1)"); + debug_assert(0); // clampf not in [0,1) return 255; } @@ -95,7 +95,7 @@ u16 u16_from_double(double in) { if(!(0.0 <= in && in < 1.0)) { - debug_warn("clampf not in [0,1)"); + debug_assert(0); // clampf not in [0,1) return 65535; } diff --git a/source/lib/lib_api.h b/source/lib/lib_api.h new file mode 100644 index 0000000000..a6cfea0b16 --- /dev/null +++ b/source/lib/lib_api.h @@ -0,0 +1,12 @@ +// note: EXTERN_C cannot be used because shared_ptr is often returned +// by value, which requires C++ linkage. + +#ifdef LIB_DLL +# ifdef LIB_BUILD +# define LIB_API __declspec(dllexport) +# else +# define LIB_API __declspec(dllimport) +# endif +#else +# define LIB_API +#endif diff --git a/source/lib/lib_errors.h b/source/lib/lib_errors.h index 2958378c8a..8926eef22c 100644 --- a/source/lib/lib_errors.h +++ b/source/lib/lib_errors.h @@ -259,7 +259,7 @@ STMT(\ ) // just pass on errors without any kind of annoying warning -// (useful for functions that can legitimately fail, e.g. vfs_exists). +// (useful for functions that can legitimately fail). #define RETURN_ERR(expression)\ STMT(\ i64 err64__ = (i64)(expression);\ diff --git a/source/lib/lockfree.cpp b/source/lib/lockfree.cpp index 8df029971b..1c9ab682cf 100644 --- a/source/lib/lockfree.cpp +++ b/source/lib/lockfree.cpp @@ -268,7 +268,7 @@ retry: // start over. this won't realistically happen, though. if(num_hps >= max_hps) { - debug_warn("max_hps overrun - why?"); + debug_assert(0); // max_hps overrun - why? goto retry; } @@ -624,7 +624,7 @@ LibError lfh_init(LFHash* hash, size_t num_entries) if(!is_pow2((long)num_entries)) { - debug_warn("lfh_init: size must be power of 2"); + debug_assert(0); // lfh_init: size must be power of 2 return ERR::INVALID_PARAM; } diff --git a/source/lib/lockfree.h b/source/lib/lockfree.h index b4b0fd29e7..943e542fae 100644 --- a/source/lib/lockfree.h +++ b/source/lib/lockfree.h @@ -11,7 +11,6 @@ #ifndef INCLUDED_LOCKFREE #define INCLUDED_LOCKFREE -#include "posix/posix_types.h" // uintptr_t #include "lib/sysdep/cpu.h" // cpu_CAS /* diff --git a/source/lib/mmgr.h b/source/lib/mmgr.h index 24cd860285..32a45cd268 100644 --- a/source/lib/mmgr.h +++ b/source/lib/mmgr.h @@ -90,6 +90,47 @@ good luck! */ + +// +// memory headers +// + +// these are all system headers that contain "new", "malloc" etc.; they must +// come before the memory tracker headers to avoid conflicts with their +// macros. therefore, they are always included, even if !CONFIG_PCH. + +#if OS_WIN +# include +# include +# include +#endif + +#include +#include +#include // operator new +#include // free() member function + + +// VC debug memory allocator / leak detector +// notes: +// - PCH is required because it makes sure system headers are included +// before redefining new (otherwise, tons of errors result); +// - disabled on ICC9 because the ICC 9.0.006 beta appears to generate +// incorrect code when we redefine new. +// TODO: remove when no longer necessary +#if MSC_VERSION && \ + (!defined(NDEBUG) || defined(TESTING)) && \ + HAVE_PCH && \ + ICC_VERSION != 900 +# define HAVE_VC_DEBUG_ALLOC 1 +#else +# define HAVE_VC_DEBUG_ALLOC 0 +#endif + + + + + #ifndef INCLUDED_MMGR #define INCLUDED_MMGR diff --git a/source/lib/ogl.cpp b/source/lib/ogl.cpp index 405415f1d4..e8b9acd26e 100644 --- a/source/lib/ogl.cpp +++ b/source/lib/ogl.cpp @@ -19,7 +19,6 @@ #include "debug.h" #include "lib/sysdep/gfx.h" #include "lib/res/h_mgr.h" -#include "lib/res/graphics/tex.h" #if MSC_VERSION #pragma comment(lib, "opengl32.lib") @@ -164,7 +163,7 @@ bool ogl_HaveVersion(const char* desired_version) int desired_major, desired_minor; if(sscanf(desired_version, "%d.%d", &desired_major, &desired_minor) != 2) { - debug_warn("invalid version string"); + debug_assert(0); // invalid version string return false; } @@ -172,7 +171,7 @@ bool ogl_HaveVersion(const char* desired_version) const char* version = (const char*)glGetString(GL_VERSION); if(!version || sscanf(version, "%d.%d", &major, &minor) != 2) { - debug_warn("GL_VERSION invalid"); + debug_assert(0); // GL_VERSION invalid return false; } @@ -267,7 +266,7 @@ static void dump_gl_error(GLenum err) void ogl_WarnIfError() { // glGetError may return multiple errors, so we poll it in a loop. - // the debug_warn should only happen once (if this is set), though. + // the debug_printf should only happen once (if this is set), though. bool error_enountered = false; GLenum first_error = 0; @@ -288,7 +287,7 @@ void ogl_WarnIfError() { char msg[64]; snprintf(msg, ARRAY_SIZE(msg), "OpenGL error(s) occurred: %04x", (int)first_error); - debug_warn(msg); + debug_printf(msg); } } #endif @@ -303,7 +302,7 @@ void ogl_WarnIfError() void ogl_SquelchError(GLenum err_to_ignore) { // glGetError may return multiple errors, so we poll it in a loop. - // the debug_warn should only happen once (if this is set), though. + // the debug_printf should only happen once (if this is set), though. bool error_enountered = false; GLenum first_error = 0; @@ -327,7 +326,7 @@ void ogl_SquelchError(GLenum err_to_ignore) { char msg[64]; snprintf(msg, ARRAY_SIZE(msg), "OpenGL error(s) occurred: %04x", (int)first_error); - debug_warn(msg); + debug_printf(msg); } } @@ -371,8 +370,7 @@ void ogl_Init() // note: this is less about performance (since the above are not // time-critical) than centralizing the 'OpenGL is ready' check. exts = (const char*)glGetString(GL_EXTENSIONS); - if(!exts) - debug_warn("called before OpenGL is ready for use"); + debug_assert(exts); // else: called before OpenGL is ready for use have_12 = ogl_HaveVersion("1.2"); have_13 = ogl_HaveVersion("1.3"); have_14 = ogl_HaveVersion("1.4"); diff --git a/source/lib/posix/posix.cpp b/source/lib/posix/posix.cpp index 17026cac3c..b20cb26b5c 100644 --- a/source/lib/posix/posix.cpp +++ b/source/lib/posix/posix.cpp @@ -1,7 +1,7 @@ #include "precompiled.h" #include "posix.h" -#if CPU_IA32 +#if ARCH_IA32 # include "lib/sysdep/ia32/ia32_asm.h" #endif @@ -10,7 +10,7 @@ float rintf(float f) { -#if CPU_IA32 +#if ARCH_IA32 return ia32_asm_rintf(f); #else return (float)(int)f; @@ -19,7 +19,7 @@ float rintf(float f) double rint(double d) { -#if CPU_IA32 +#if ARCH_IA32 return ia32_asm_rint(d); #else return (double)(int)d; @@ -29,7 +29,7 @@ double rint(double d) float fminf(float a, float b) { -#if CPU_IA32 +#if ARCH_IA32 return ia32_asm_fminf(a, b); #else return (a < b)? a : b; @@ -38,7 +38,7 @@ float fminf(float a, float b) float fmaxf(float a, float b) { -#if CPU_IA32 +#if ARCH_IA32 return ia32_asm_fmaxf(a, b); #else return (a > b)? a : b; @@ -48,7 +48,7 @@ float fmaxf(float a, float b) uint fpclassifyd(double d) { -#if CPU_IA32 +#if ARCH_IA32 return ia32_asm_fpclassifyd(d); #else // really sucky stub implementation; doesn't attempt to cover all cases. @@ -62,7 +62,7 @@ uint fpclassifyd(double d) uint fpclassifyf(float f) { -#if CPU_IA32 +#if ARCH_IA32 return ia32_asm_fpclassifyf(f); #else const double d = (double)f; @@ -73,19 +73,7 @@ uint fpclassifyf(float f) #endif // #if !HAVE_C99_MATH -#if !HAVE_STRDUP -char* strdup(const char* str) -{ - const size_t num_chars = strlen(str); - char* dst = (char*)malloc((num_chars+1)*sizeof(char)); // note: strdup is required to use malloc - if(!dst) - return 0; - SAFE_STRCPY(dst, str); - return dst; -} -#endif - -#if !HAVE_WCSDUP +#if EMULATE_WCSDUP wchar_t* wcsdup(const wchar_t* str) { const size_t num_chars = wcslen(str); @@ -95,4 +83,4 @@ wchar_t* wcsdup(const wchar_t* str) SAFE_WCSCPY(dst, str); return dst; } -#endif // #if !HAVE_WCSDUP +#endif diff --git a/source/lib/posix/posix.h b/source/lib/posix/posix.h index 76141c46de..babe1cb29f 100644 --- a/source/lib/posix/posix.h +++ b/source/lib/posix/posix.h @@ -81,15 +81,24 @@ need only be renamed (e.g. _open, _stat). #define strncasecmp strnicmp #endif - -#if !HAVE_STRDUP -extern char* strdup(const char* str); +#if OS_MACOSX +# define EMULATE_WCSDUP 1 +#else +# define EMULATE_WCSDUP 0 #endif -#if !HAVE_WCSDUP + +#if EMULATE_WCSDUP extern wchar_t* wcsdup(const wchar_t* str); #endif +// rint*, fminf, fpclassify (too few/diverse to make separate HAVE_ for each) +#if HAVE_C99 || GCC_VERSION +# define HAVE_C99_MATH 1 +#else +# define HAVE_C99_MATH 0 +#endif + #if !HAVE_C99_MATH // round float to nearest integral value, according to // current rounding mode. diff --git a/source/lib/posix/posix_filesystem.h b/source/lib/posix/posix_filesystem.h index c097bf46f6..420effb847 100644 --- a/source/lib/posix/posix_filesystem.h +++ b/source/lib/posix/posix_filesystem.h @@ -7,7 +7,3 @@ #endif #include "posix_errno.h" // for user convenience - -#if !HAVE_MKDIR -extern int mkdir(const char* path, mode_t mode); -#endif diff --git a/source/lib/precompiled.h b/source/lib/precompiled.h index 103afe0357..440f3315f7 100644 --- a/source/lib/precompiled.h +++ b/source/lib/precompiled.h @@ -3,21 +3,20 @@ * File : precompiled.h * Project : 0 A.D. * Description : precompiled header. must be the first non-comment part - * : of every source file (VC6/7 requirement). + * : of every source file (VC6..8 requirement). * ========================================================================= */ // license: GPL; see lib/license.txt -// if the compiler supports PCH (i.e. HAVE_PCH is defined), this -// tries to include all headers that may be needed. otherwise, all source -// files will still need to include this (for various global fixes and the -// memory trackers), but additionally include all required headers. -// -// this policy yields the best compile performance with or without PCH. +// if PCHs are supported and enabled, we make an effort to include all +// system headers. otherwise, only a few headers (e.g. memory tracker) +// are pulled in and source files must include all the system headers +// they use. this policy ensures good compile performance whether or not +// PCHs are being used. -// must come before warning disables. -#include "lib/config.h" +#include "lib/config.h" // CONFIG_ENABLE_PCH +#include "lib/sysdep/compiler.h" // HAVE_PCH // disable some common and annoying warnings // (done as soon as possible so that headers below are covered) @@ -39,53 +38,48 @@ # endif #endif + // // headers made available everywhere for convenience // +#include "lib/sysdep/compiler.h" +#include "lib/sysdep/stl.h" +#include "lib/sysdep/os.h" +#include "lib/sysdep/arch.h" + +#include "lib/lib_api.h" + #include "lib/types.h" #include "lib/lib.h" #include "lib/lib_errors.h" #include "lib/secure_crt.h" #include "lib/debug.h" #include "lib/code_annotation.h" -#include "lib/sysdep/compiler.h" -#include "lib/sysdep/stl.h" + +// Boost +#include // noncopyable +#include +#include +#include +#pragma warning(push, 3) // filesystem isn't W4-clean +#include +#pragma warning(pop) +using boost::shared_ptr; // has been added to TR1 +namespace fs = boost::filesystem; + +// (this must come after boost and common lib headers) #include "lib/posix/posix.h" // -// memory headers +// precompiled headers // -// these are all system headers that contain "new", "malloc" etc.; they must -// come before the memory tracker headers to avoid conflicts with their -// macros. therefore, they are always included, even if !HAVE_PCH. +#if CONFIG_ENABLE_PCH && HAVE_PCH -#if OS_WIN -# include -#endif - -#include -#include -#include // free() member function - - -// -// headers to be precompiled -// - -// candidates are all system headers we may possibly need or large/rarely -// changed project headers; everything placed in here will not need to be -// compiled every time. however, if they change, the project will have to be -// completely rebuilt. (slow!) -// -// if the compiler doesn't support precompiled headers (i.e. !HAVE_PCH), -// including anything here would actually slow things down, because we might -// not otherwise need some of these headers. therefore, do nothing and rely -// on all source files (additionally) including everything they need. - -#if HAVE_PCH +// anything placed here won't need to be compiled in each translation unit, +// but will cause a complete rebuild if they change. // all new-form C library headers #include @@ -96,8 +90,7 @@ #include #include #include -// Including setjmp.h here causes incompatibilities with libpng on Debian/Ubuntu -//#include +//#include // incompatible with libpng on Debian/Ubuntu #include #include #include @@ -154,32 +147,13 @@ # include #endif -// (further headers to be precompiled go here) - -#endif // #if HAVE_PCH +#endif // #if CONFIG_PCH // restore temporarily-disabled warnings #if MSC_VERSION # pragma warning(default:4702) #endif - -// -// memory trackers -// - -// these must be included from every file to make sure all allocations -// are hooked. placing in the precompiled header is more convenient than -// manually #including from every file, but requires that all system -// headers containing "new", "malloc" etc. come before this (see above). -// -// note: mmgr.h activates mmgr or the VC debug heap or nothing depending -// on CONFIG_USE_MMGR and HAVE_VC_DEBUG_ALLOC settings. -# include "mmgr.h" - - -// Boost -#include // noncopyable -#include -#include -#include +// (this must be included from every file to make sure all allocations +// are tracked; placing it in the PCH is a convenient means of doing so) +#include "mmgr.h" diff --git a/source/lib/secure_crt.cpp b/source/lib/secure_crt.cpp index 8f7e3e3c57..0520862009 100644 --- a/source/lib/secure_crt.cpp +++ b/source/lib/secure_crt.cpp @@ -79,16 +79,13 @@ ERROR_ASSOCIATE(ERR::STRING_NOT_TERMINATED, "Invalid string (no 0 terminator fou // currently disabled due to high risk of false positives. #define WARN_IF_PTR_LEN(len)\ /* -STMT( \ - if(len == sizeof(char*)) \ - debug_warn("make sure string buffer size is correct");\ -)*/ + debug_assert(len != sizeof(char*)); +*/ // skip our implementation if already available, but not the // self-test and the t* defines (needed for test). -#if !HAVE_SECURE_CRT - +#if EMULATE_SECURE_CRT #if !OS_UNIX || OS_MACOSX // return length [in characters] of a string, not including the trailing @@ -237,4 +234,4 @@ errno_t tfopen_s(FILE** pfile, const tchar* filename, const tchar* mode) } #endif -#endif // #if !HAVE_SECURE_CRT +#endif // #if EMULATE_SECURE_CRT diff --git a/source/lib/secure_crt.h b/source/lib/secure_crt.h index d2cb3e1deb..e6cbf02b71 100644 --- a/source/lib/secure_crt.h +++ b/source/lib/secure_crt.h @@ -11,16 +11,20 @@ #ifndef INCLUDED_SECURE_CRT #define INCLUDED_SECURE_CRT -#include "posix/posix_types.h" // size_t - namespace ERR { const LibError STRING_NOT_TERMINATED = -100600; } -// only declare these functions if using our implementation -// (otherwise, we risk incompatibilities) -#if !HAVE_SECURE_CRT +// if the platform lacks a secure CRT implementation, we'll provide one. +#if MSC_VERSION >= 1400 +# define EMULATE_SECURE_CRT 0 +#else +# define EMULATE_SECURE_CRT 1 +#endif + + +#if EMULATE_SECURE_CRT // (conflicts with glibc definitions) #if !OS_UNIX @@ -78,5 +82,5 @@ extern errno_t _wfopen_s(FILE** pfile, const wchar_t* filename, const wchar_t* m #define fscanf_s fscanf -#endif // #if !HAVE_SECURE_CRT +#endif // #if EMULATE_SECURE_CRT #endif // #ifndef INCLUDED_SECURE_CRT diff --git a/source/lib/sysdep/compiler.h b/source/lib/sysdep/compiler.h index ae16506db7..419c2d8107 100644 --- a/source/lib/sysdep/compiler.h +++ b/source/lib/sysdep/compiler.h @@ -11,6 +11,38 @@ #ifndef INCLUDED_COMPILER #define INCLUDED_COMPILER +#include "config.h" // CONFIG_OMIT_FP + + +// detect compiler and its version (0 if not present, otherwise +// major*100 + minor). note that more than one *_VERSION may be +// non-zero due to interoperability (e.g. ICC with MSC). +// .. VC +#ifdef _MSC_VER +# define MSC_VERSION _MSC_VER +#else +# define MSC_VERSION 0 +#endif +// .. ICC (VC-compatible) +#if defined(__INTEL_COMPILER) +# define ICC_VERSION __INTEL_COMPILER +#else +# define ICC_VERSION 0 +#endif +// .. LCC (VC-compatible) +#if defined(__LCC__) +# define LCC_VERSION __LCC__ +#else +# define LCC_VERSION 0 +#endif +// .. GCC +#ifdef __GNUC__ +# define GCC_VERSION (__GNUC__*100 + __GNUC_MINOR__) +#else +# define GCC_VERSION 0 +#endif + + // pass "omit frame pointer" setting on to the compiler #if MSC_VERSION # if CONFIG_OMIT_FP @@ -23,6 +55,16 @@ #endif +// are PreCompiled Headers supported? +#if MSC_VERSION +# define HAVE_PCH 1 +#elif defined(USING_PCH) +# define HAVE_PCH 1 +#else +# define HAVE_PCH 0 +#endif + + // try to define _W64, if not already done // (this is useful for catching pointer size bugs) #ifndef _W64 @@ -36,6 +78,29 @@ #endif +// check if compiling in pure C mode (not C++) with support for C99. +// (this is more convenient than testing __STDC_VERSION__ directly) +// +// note: C99 provides several useful but disjunct bits of functionality. +// unfortunately, most C++ compilers do not offer a complete implementation. +// however, many of these features are likely to be added to C++, and/or are +// already available as extensions. what we'll do is add a HAVE_ macro for +// each feature and test those instead. they are set if HAVE_C99, or also if +// the compiler happens to support something compatible. +// +// rationale: lying about __STDC_VERSION__ via Premake so as to enable support +// for some C99 functions doesn't work. Mac OS X headers would then use the +// restrict keyword, which is never supported by g++ (because that might +// end up breaking valid C++98 programs). +#define HAVE_C99 0 +#ifdef __STDC_VERSION__ +# if __STDC_VERSION__ >= 199901L +# undef HAVE_C99 +# define HAVE_C99 1 +# endif +#endif + + // C99-like restrict (non-standard in C++, but widely supported in various forms). // // May be used on pointers. May also be used on member functions to indicate @@ -99,6 +164,7 @@ # define ASSUME_UNREACHABLE #endif + // extern "C", but does the right thing in pure-C mode #if defined(__cplusplus) # define EXTERN_C extern "C" diff --git a/source/lib/sysdep/cpu.h b/source/lib/sysdep/cpu.h index 4156d34cad..b8ca8091be 100644 --- a/source/lib/sysdep/cpu.h +++ b/source/lib/sysdep/cpu.h @@ -151,7 +151,9 @@ extern void cpu_ConfigureFloatingPoint(); // convert float to int much faster than _ftol2, which would normally be // used by (int) casts. -#if !USE_IA32_FLOAT_TO_INT +// should we use our float->int code? newer GCC versions with -ffast-math and +// VC8 can use SSE, so skip it there. +#if ARCH_IA32 && MSC_VERSION && MSC_VERSION < 1400 #define cpu_i32FromFloat(f) ((i32)f) #define cpu_i32FromDouble(d) ((i32)d) #define cpu_i64FromDouble(d) ((i64)d) @@ -172,7 +174,7 @@ extern i64 cpu_i64FromDouble(double d); * casting in user code. **/ template -extern bool cpu_CAS(volatile T* location, T expected, T new_value) +bool cpu_CAS(volatile T* location, T expected, T new_value) { return cpu_CAS((volatile uintptr_t*)location, (uintptr_t)expected, (uintptr_t)new_value); } diff --git a/source/lib/sysdep/ia32/ia32.h b/source/lib/sysdep/ia32/ia32.h index 2279de97c2..84547596e7 100644 --- a/source/lib/sysdep/ia32/ia32.h +++ b/source/lib/sysdep/ia32/ia32.h @@ -11,8 +11,8 @@ #ifndef INCLUDED_IA32 #define INCLUDED_IA32 -#if !CPU_IA32 -#error "including ia32.h without CPU_IA32=1" +#if !ARCH_IA32 +#error "including ia32.h without ARCH_IA32=1" #endif #include "ia32_asm.h" @@ -35,7 +35,7 @@ extern Ia32Vendor ia32_Vendor(); /** * @return the colloquial processor generation - * (6 = Pentium II / K6, 7 = Pentium III / Athlon, 8 = Opteron) + * (5 = Pentium, 6 = Pentium Pro/II/III / K6, 7 = Pentium4 / Athlon, 8 = Core / Opteron) **/ extern uint ia32_Generation(); diff --git a/source/lib/sysdep/stl.h b/source/lib/sysdep/stl.h index e918c788bc..005c43706b 100644 --- a/source/lib/sysdep/stl.h +++ b/source/lib/sysdep/stl.h @@ -11,10 +11,44 @@ #ifndef INCLUDED_STL #define INCLUDED_STL -// compiling without exceptions (usually for performance reasons); -// tell STL not to generate any. +#include "config.h" +#include "compiler.h" + +// detect STL version +// .. Dinkumware +#if MSC_VERSION +# include // defines _CPPLIB_VER +#endif +#if defined(_CPPLIB_VER) +# define STL_DINKUMWARE _CPPLIB_VER +#else +# define STL_DINKUMWARE 0 +#endif +// .. GCC +#if defined(__GLIBCPP__) +# define STL_GCC __GLIBCPP__ +#elif defined(__GLIBCXX__) +# define STL_GCC __GLIBCXX__ +#else +# define STL_GCC 0 +#endif +// .. ICC +#if defined(__INTEL_CXXLIB_ICC) +# define STL_ICC __INTEL_CXXLIB_ICC +#else +# define STL_ICC 0 +#endif + + +// disable (slow!) iterator checks in release builds (unless someone already defined this) +#if STL_DINKUMWARE && defined(NDEBUG) && !defined(_SECURE_SCL) +# define _SECURE_SCL 0 +#endif + + +// pass "disable exceptions" setting on to the STL #if CONFIG_DISABLE_EXCEPTIONS -# if OS_WIN +# if STL_DINKUMWARE # define _HAS_EXCEPTIONS 0 # else # define STL_NO_EXCEPTIONS @@ -26,7 +60,8 @@ // these containers are useful but not part of C++98. most STL vendors // provide them in some form; we hide their differences behind macros. -#if GCC_VERSION +#if STL_GCC + # include # include @@ -76,7 +111,7 @@ namespace __gnu_cxx }; } -#else // !__GNUC__ +#elif STL_DINKUMWARE # include # include @@ -96,6 +131,15 @@ namespace __gnu_cxx # define STL_HASH_VALUE std::hash_value # endif // MSC_VERSION >= 1300 -#endif // !__GNUC__ +#endif + + +// nonstandard STL containers +#define HAVE_STL_SLIST 0 +#if STL_DINKUMWARE +# define HAVE_STL_HASH 1 +#else +# define HAVE_STL_HASH 0 +#endif #endif // #ifndef INCLUDED_STL diff --git a/source/lib/sysdep/unix/x/x.cpp b/source/lib/sysdep/unix/x/x.cpp index e0bfb1d00f..29df76a448 100644 --- a/source/lib/sysdep/unix/x/x.cpp +++ b/source/lib/sysdep/unix/x/x.cpp @@ -4,6 +4,12 @@ #include "precompiled.h" +#if OS_LINUX +# define HAVE_X 1 +#else +# define HAVE_X 0 +#endif + #if HAVE_X #include diff --git a/source/lib/sysdep/win/error_dialog.rc b/source/lib/sysdep/win/error_dialog.rc index 367bedf560..24fb4e74e6 100644 --- a/source/lib/sysdep/win/error_dialog.rc +++ b/source/lib/sysdep/win/error_dialog.rc @@ -1,35 +1,23 @@ -// Microsoft Visual C++ generated resource script. -// #include "error_dialog.h" #include - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL #pragma code_page(1252) #endif //_WIN32 -#ifdef APSTUDIO_INVOKED + ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // +#ifdef APSTUDIO_INVOKED 1 TEXTINCLUDE BEGIN "error_dialog.h\0" END -2 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - #endif // APSTUDIO_INVOKED @@ -39,15 +27,13 @@ END // IDD_DIALOG1 DIALOGEX 0, 0, 328, 260 -STYLE DS_SETFONT | DS_SETFOREGROUND | DS_FIXEDSYS | WS_MINIMIZEBOX | - WS_MAXIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -EXSTYLE WS_EX_TOPMOST | WS_EX_APPWINDOW +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_APPWINDOW CAPTION "Program Error" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN PUSHBUTTON "&Continue",IDC_CONTINUE,7,239,50,14 - EDITTEXT IDC_EDIT1,7,7,314,204,ES_MULTILINE | ES_READONLY | - ES_WANTRETURN | WS_VSCROLL + EDITTEXT IDC_EDIT1,7,7,314,204,ES_MULTILINE | ES_READONLY | ES_WANTRETURN | WS_VSCROLL PUSHBUTTON "Copy",IDC_COPY,271,212,50,14 PUSHBUTTON "&Suppress",IDC_SUPPRESS,59,239,50,14 PUSHBUTTON "&Break",IDC_BREAK,111,239,50,14 @@ -73,6 +59,4 @@ BEGIN END #endif // APSTUDIO_INVOKED -#endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// - diff --git a/source/lib/sysdep/win/wdbg.cpp b/source/lib/sysdep/win/wdbg.cpp index e89fb199f0..ccb2aaf7e6 100644 --- a/source/lib/sysdep/win/wdbg.cpp +++ b/source/lib/sysdep/win/wdbg.cpp @@ -30,8 +30,10 @@ static void unlock() static NT_TIB* get_tib() { -#if CPU_IA32 +#if ARCH_IA32 NT_TIB* tib; + // ICC 10 doesn't support the NT_TIB.Self syntax, so we have to use + // a constant (asm code isn't 64-bit safe anyway). __asm { mov eax, fs:[NT_TIB.Self] @@ -126,7 +128,7 @@ static void* while_suspended_thread_func(void* user_arg) // abort, since GetThreadContext only works if the target is suspended. if(err == (DWORD)-1) { - debug_warn("while_suspended_thread_func: SuspendThread failed"); + debug_assert(0); // while_suspended_thread_func: SuspendThread failed return (void*)(intptr_t)-1; } // target is now guaranteed to be suspended, @@ -251,8 +253,7 @@ have_reg: rw = 1; break; case DBG_BREAK_DATA_WRITE: rw = 3; break; - default: - debug_warn("invalid type"); + NODEFAULT; } // .. length (determine from addr's alignment). // note: IA-32 requires len=0 for code breakpoints. @@ -296,7 +297,7 @@ static LibError brk_do_request(HANDLE hThread, void* arg) if(!GetThreadContext(hThread, &context)) WARN_RETURN(ERR::FAIL); -#if CPU_IA32 +#if ARCH_IA32 if(bi->want_all_disabled) ret = brk_disable_all_in_ctx(bi, &context); else @@ -354,7 +355,7 @@ LibError debug_remove_all_breaks() // but can be used to filter out obviously wrong values in a portable manner. int debug_is_pointer_bogus(const void* p) { -#if CPU_IA32 +#if ARCH_IA32 if(p < (void*)0x10000) return true; if(p >= (void*)(uintptr_t)0x80000000) @@ -440,6 +441,6 @@ void wdbg_set_thread_name(const char* name) __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"); + debug_assert(0); // thread name hack doesn't work under this debugger } } diff --git a/source/lib/sysdep/win/wdbg.h b/source/lib/sysdep/win/wdbg.h index b10950b46a..4969f75a7e 100644 --- a/source/lib/sysdep/win/wdbg.h +++ b/source/lib/sysdep/win/wdbg.h @@ -11,7 +11,7 @@ #ifndef INCLUDED_WDBG #define INCLUDED_WDBG -#if HAVE_MS_ASM +#if MSC_VERSION # define debug_break() __asm { int 3 } #else # error "port this or define to implementation function" diff --git a/source/lib/sysdep/win/wdbg_sym.cpp b/source/lib/sysdep/win/wdbg_sym.cpp index ddc1b04b6e..c93aadef45 100644 --- a/source/lib/sysdep/win/wdbg_sym.cpp +++ b/source/lib/sysdep/win/wdbg_sym.cpp @@ -21,7 +21,7 @@ #include "lib/debug_stl.h" #include "lib/app_hooks.h" #include "lib/path_util.h" -#if CPU_IA32 +#if ARCH_IA32 # include "lib/sysdep/ia32/ia32.h" #endif #include "win.h" @@ -261,7 +261,7 @@ func2: */ -#if CPU_IA32 && !CONFIG_OMIT_FP +#if ARCH_IA32 && !CONFIG_OMIT_FP static LibError ia32_walk_stack(STACKFRAME64* sf) { @@ -299,7 +299,7 @@ static LibError ia32_walk_stack(STACKFRAME64* sf) return INFO::OK; } -#endif // #if CPU_IA32 && !CONFIG_OMIT_FP +#endif // #if ARCH_IA32 && !CONFIG_OMIT_FP // called for each stack frame found by walk_stack, passing information @@ -351,7 +351,7 @@ static LibError walk_stack(StackFrameCallback cb, void* user_arg = 0, uint skip // this MUST be done inline and not in an external function because // compiler-generated prolog code trashes some registers. -#if CPU_IA32 +#if ARCH_IA32 ia32_asm_GetCurrentContext(&context); #else // preferred implementation (was imported during module init) @@ -384,7 +384,7 @@ static LibError walk_stack(StackFrameCallback cb, void* user_arg = 0, uint skip sf.AddrPC.Mode = AddrModeFlat; sf.AddrFrame.Mode = AddrModeFlat; sf.AddrStack.Mode = AddrModeFlat; -#if CPU_AMD64 +#if ARCH_AMD64 sf.AddrPC.Offset = pcontext->Rip; sf.AddrFrame.Offset = pcontext->Rbp; sf.AddrStack.Offset = pcontext->Rsp; @@ -412,7 +412,7 @@ static LibError walk_stack(StackFrameCallback cb, void* user_arg = 0, uint skip // code is authoritative provided its prerequisite (FP not omitted) // is met, otherwise totally unusable. LibError err; -#if CPU_IA32 && !CONFIG_OMIT_FP +#if ARCH_IA32 && !CONFIG_OMIT_FP err = ia32_walk_stack(&sf); #else sym_init(); @@ -420,7 +420,7 @@ static LibError walk_stack(StackFrameCallback cb, void* user_arg = 0, uint skip // so we have to reset it and check for 0. *sigh* SetLastError(0); const HANDLE hThread = GetCurrentThread(); - BOOL ok = StackWalk64(machine, hProcess, hThread, &sf, pcontext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0); + BOOL ok = StackWalk64(machine, hProcess, hThread, &sf, (PVOID)pcontext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0); // note: don't use LibError_from_win32 because it raises a warning, // and this "fails" commonly (when no stack frames are left). err = ok? INFO::OK : ERR::FAIL; @@ -561,7 +561,7 @@ static void out(const wchar_t* fmt, ...) // make sure out_chars_left remains nonnegative if((size_t)len > out_chars_left) { - debug_warn("apparently wrote more than out_chars_left"); + debug_assert(0); // apparently wrote more than out_chars_left len = (int)out_chars_left; } out_chars_left -= len; @@ -926,11 +926,7 @@ static LibError determine_symbol_address(DWORD id, DWORD UNUSED(type_id), const case DataIsGlobal: break; - default: - debug_warn("unexpected data_kind"); - - //case DataIsConstant - + NODEFAULT; } // get SYMBOL_INFO (we need .Flags) @@ -1069,7 +1065,7 @@ static LibError dump_sym_base_type(DWORD type_id, const u8* p, DumpState state) if(size == sizeof(float)) *(double*)&data = (double)*(float*)&data; else if(size != sizeof(double)) - debug_warn("dump_sym_base_type: invalid float size"); + debug_assert(0); // invalid float size fmt = L"%g"; break; @@ -1077,7 +1073,7 @@ static LibError dump_sym_base_type(DWORD type_id, const u8* p, DumpState state) case btInt: case btLong: if(size != 1 && size != 2 && size != 4 && size != 8) - debug_warn("dump_sym_base_type: invalid int size"); + debug_assert(0); // invalid int size // need to re-load and sign-extend, because we output 64 bits. data = movsx_le64(p, size); fmt = L"%I64d"; @@ -1107,7 +1103,7 @@ display_as_hex: else if(size == 8) fmt = L"0x%016I64X"; else - debug_warn("dump_sym_base_type: invalid uint size"); + debug_assert(0); // invalid uint size break; // character @@ -1135,7 +1131,7 @@ display_as_hex: fmt = L""; } else - debug_warn("dump_sym_base_type: non-pointer btVoid or btNoType"); + debug_assert(0); // non-pointer btVoid or btNoType break; default: @@ -1866,6 +1862,9 @@ static LibError dump_frame_cb(const STACKFRAME64* sf, void* UNUSED(user_arg)) LibError debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void* pcontext) { +buf='\0'; +return INFO::OK; + static uintptr_t already_in_progress; if(!cpu_CAS(&already_in_progress, 0, 1)) return ERR::REENTERED; // NOWARN diff --git a/source/lib/sysdep/win/wdir_watch.cpp b/source/lib/sysdep/win/wdir_watch.cpp index a086b77f76..cf6a8d0e40 100644 --- a/source/lib/sysdep/win/wdir_watch.cpp +++ b/source/lib/sysdep/win/wdir_watch.cpp @@ -232,7 +232,7 @@ LibError dir_add_watch(const char* dir, intptr_t* preqnum) // need it before binding dir to IOCP because it is our "key". if(last_reqnum == INT_MAX) { - debug_warn("request numbers are no longer unique"); + debug_assert(0); // request numbers are no longer unique CloseHandle(hDir); goto fail; } diff --git a/source/lib/sysdep/win/whrt/counter.cpp b/source/lib/sysdep/win/whrt/counter.cpp index 486281986c..b54a45b7ac 100644 --- a/source/lib/sysdep/win/whrt/counter.cpp +++ b/source/lib/sysdep/win/whrt/counter.cpp @@ -85,7 +85,7 @@ ICounter* CreateCounter(uint id) // size until after the alloc / placement new. if(!cpu_CAS(&isCounterAllocated, 0, 1)) - debug_warn("static counter memory is already in use!"); + debug_assert(0); // static counter memory is already in use! static const size_t memSize = 200; static u8 mem[memSize]; diff --git a/source/lib/sysdep/win/whrt/hpet.cpp b/source/lib/sysdep/win/whrt/hpet.cpp index 4353a9bbaf..2141546dc5 100644 --- a/source/lib/sysdep/win/whrt/hpet.cpp +++ b/source/lib/sysdep/win/whrt/hpet.cpp @@ -2,7 +2,7 @@ * ========================================================================= * File : hpet.cpp * Project : 0 A.D. - * Description : Timer implementation using timeGetTime + * Description : Timer implementation using High Precision Event Timer * ========================================================================= */ diff --git a/source/lib/sysdep/win/whrt/hpet.h b/source/lib/sysdep/win/whrt/hpet.h index cfea652ff3..f85c922ec2 100644 --- a/source/lib/sysdep/win/whrt/hpet.h +++ b/source/lib/sysdep/win/whrt/hpet.h @@ -2,7 +2,7 @@ * ========================================================================= * File : hpet.h * Project : 0 A.D. - * Description : Timer implementation using timeGetTime + * Description : Timer implementation using High Precision Event Timer * ========================================================================= */ diff --git a/source/lib/sysdep/win/win.h b/source/lib/sysdep/win/win.h index aea6aa1491..8901eaf47b 100644 --- a/source/lib/sysdep/win/win.h +++ b/source/lib/sysdep/win/win.h @@ -326,7 +326,7 @@ enum DataKind -#if CPU_IA32 +#if ARCH_IA32 // the official version causes pointer truncation warnings. # undef InterlockedExchangePointer # define InterlockedExchangePointer(Target, Value) (PVOID)(uintptr_t)InterlockedExchange((PLONG)(Target), (LONG)(uintptr_t)(Value)) diff --git a/source/lib/sysdep/win/wmi.cpp b/source/lib/sysdep/win/wmi.cpp index db72804caf..79b76d2eef 100644 --- a/source/lib/sysdep/win/wmi.cpp +++ b/source/lib/sysdep/win/wmi.cpp @@ -21,18 +21,18 @@ WINIT_REGISTER_EARLY_INIT(wmi_Init); WINIT_REGISTER_EARLY_SHUTDOWN(wmi_Shutdown); -static IWbemLocator* pLoc; static IWbemServices* pSvc; +_COM_SMARTPTR_TYPEDEF(IWbemLocator, __uuidof(IWbemLocator)); +_COM_SMARTPTR_TYPEDEF(IWbemClassObject, __uuidof(IWbemClassObject)); +_COM_SMARTPTR_TYPEDEF(IEnumWbemClassObject, __uuidof(IEnumWbemClassObject)); + + static LibError wmi_Init() { HRESULT hr; - // initializing with COINIT_MULTITHREADED causes the (unchanged) value of - // pSvc to be invalid by the time wmi_Shutdown is reached. the cause is - // unclear (maybe another DLL already doing CoUninitialize?), but using - // single-threaded apartment mode (the default) avoids it. - hr = CoInitialize(0); + hr = CoInitializeEx(0, COINIT_MULTITHREADED); if(FAILED(hr)) WARN_RETURN(ERR::_1); @@ -40,13 +40,16 @@ static LibError wmi_Init() if(FAILED(hr)) WARN_RETURN(ERR::_2); - hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (void**)&pLoc); - if(FAILED(hr)) - WARN_RETURN(ERR::_3); + { + IWbemLocatorPtr pLoc = 0; + hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (void**)&pLoc); + if(FAILED(hr)) + WARN_RETURN(ERR::_3); - hr = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), 0, 0, 0, 0, 0, 0, &pSvc); - if(FAILED(hr)) - WARN_RETURN(ERR::_4); + hr = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), 0, 0, 0, 0, 0, 0, &pSvc); + if(FAILED(hr)) + WARN_RETURN(ERR::_4); + } hr = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, 0, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, 0, EOAC_NONE); if(FAILED(hr)) @@ -58,10 +61,10 @@ static LibError wmi_Init() static LibError wmi_Shutdown() { - pSvc->Release(); - pLoc->Release(); - CoUninitialize(); - + // the memory pointed to by pSvc is already invalidated at this point; + // maybe some other module has already wiped out COM? + //pSvc->Release(); + //CoUninitialize(); return INFO::OK; } @@ -70,7 +73,7 @@ LibError wmi_GetClass(const char* className, WmiMap& wmiMap) { HRESULT hr; - IEnumWbemClassObject* pEnum = 0; + IEnumWbemClassObjectPtr pEnum = 0; char query[200]; sprintf_s(query, ARRAY_SIZE(query), "SELECT * FROM %s", className); hr = pSvc->ExecQuery(L"WQL", _bstr_t(query), WBEM_FLAG_FORWARD_ONLY|WBEM_FLAG_RETURN_IMMEDIATELY, 0, &pEnum); @@ -80,7 +83,7 @@ LibError wmi_GetClass(const char* className, WmiMap& wmiMap) for(;;) { - IWbemClassObject* pObj = 0; + IWbemClassObjectPtr pObj = 0; ULONG numReturned = 0; hr = pEnum->Next(WBEM_INFINITE, 1, &pObj, &numReturned); if(FAILED(hr)) @@ -103,9 +106,7 @@ LibError wmi_GetClass(const char* className, WmiMap& wmiMap) wmiMap[name] = value; SysFreeString(name); } - pObj->Release(); } - pEnum->Release(); return INFO::OK; } diff --git a/source/lib/sysdep/win/wposix/wdlfcn.cpp b/source/lib/sysdep/win/wposix/wdlfcn.cpp index 038db8d517..76a66ee70b 100644 --- a/source/lib/sysdep/win/wposix/wdlfcn.cpp +++ b/source/lib/sysdep/win/wposix/wdlfcn.cpp @@ -32,8 +32,7 @@ char* dlerror(void) void* dlopen(const char* so_name, int flags) { - if(flags & RTLD_GLOBAL) - debug_warn("dlopen: unsupported flag(s)"); + debug_assert(!(flags & RTLD_GLOBAL)); // if present, strip .so extension; add .dll extension char dll_name[MAX_PATH]; @@ -45,8 +44,7 @@ void* dlopen(const char* so_name, int flags) SAFE_STRCPY(ext, "dll"); HMODULE hModule = LoadLibrary(dll_name); - if(!hModule) - debug_warn("dlopen failed"); + debug_assert(hModule); return void_from_HMODULE(hModule); } @@ -55,8 +53,6 @@ void* dlsym(void* handle, const char* sym_name) { HMODULE hModule = HMODULE_from_void(handle); void* sym = GetProcAddress(hModule, sym_name); - if(!sym) - debug_warn("dlsym failed"); + debug_assert(sym); return sym; } - diff --git a/source/lib/sysdep/win/wposix/wfilesystem.cpp b/source/lib/sysdep/win/wposix/wfilesystem.cpp index 0a8e041022..e4df6a3220 100644 --- a/source/lib/sysdep/win/wposix/wfilesystem.cpp +++ b/source/lib/sysdep/win/wposix/wfilesystem.cpp @@ -244,7 +244,7 @@ DIR* opendir(const char* path) { errno = ENOENT; fail: - debug_warn("opendir failed"); + debug_assert(0); return 0; } @@ -329,7 +329,7 @@ fail: if(GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err); else - debug_warn("readdir: FindNextFile failed"); + debug_assert(0); // readdir: FindNextFile failed return 0; } diff --git a/source/lib/sysdep/win/wposix/wfilesystem.h b/source/lib/sysdep/win/wposix/wfilesystem.h index 096293a9c3..fbddd2169d 100644 --- a/source/lib/sysdep/win/wposix/wfilesystem.h +++ b/source/lib/sysdep/win/wposix/wfilesystem.h @@ -18,8 +18,6 @@ typedef unsigned int mode_t; #endif -// mkdir is defined by posix_filesystem #if !HAVE_MKDIR - // (christmas-tree values because mkdir mode is ignored anyway) #define S_IRWXO 0xFFFF #define S_IRWXU 0xFFFF @@ -28,6 +26,17 @@ typedef unsigned int mode_t; #define S_ISDIR(m) (m & S_IFDIR) #define S_ISREG(m) (m & S_IFREG) +// we need to emulate this on VC7 (not included) and VC8 (deprecated) +#if MSC_VERSION +# define EMULATE_MKDIR 1 +#else +# define EMULATE_MKDIR 0 +#endif + +#if EMULATE_MKDIR +extern int mkdir(const char* path, mode_t mode); +#endif + // // dirent.h diff --git a/source/lib/sysdep/win/wposix/wmman.cpp b/source/lib/sysdep/win/wposix/wmman.cpp index d68f11a337..db942fc6e6 100644 --- a/source/lib/sysdep/win/wposix/wmman.cpp +++ b/source/lib/sysdep/win/wposix/wmman.cpp @@ -35,8 +35,10 @@ static DWORD win32_prot(int prot) return PAGE_EXECUTE_READWRITE; case PROT_READ|PROT_WRITE|PROT_EXEC: return PAGE_EXECUTE_READWRITE; - NODEFAULT; + NODEFAULT; } + + UNREACHABLE; } @@ -74,7 +76,7 @@ static LibError mmap_mem(void* start, size_t len, int prot, int flags, int fd, v WARN_IF_FALSE(VirtualFree(start, len, MEM_DECOMMIT)); *pp = 0; // make sure *pp won't be misinterpreted as an error - cassert(MAP_FAILED != 0); + cassert(MAP_FAILED); return INFO::OK; } } @@ -126,8 +128,7 @@ static LibError mmap_file_access(int prot, int flags, DWORD& flProtect, DWORD& d } -static LibError mmap_file(void* start, size_t len, int prot, int flags, - int fd, off_t ofs, void** pp) +static LibError mmap_file(void* start, size_t len, int prot, int flags, int fd, off_t ofs, void** pp) { debug_assert(fd != -1); // handled by mmap_mem diff --git a/source/lib/sysdep/win/wposix/wmman.h b/source/lib/sysdep/win/wposix/wmman.h index 344283111d..d9ef04f9ab 100644 --- a/source/lib/sysdep/win/wposix/wmman.h +++ b/source/lib/sysdep/win/wposix/wmman.h @@ -27,7 +27,7 @@ // doesn't commit mmap-ed regions anyway, but we specify this flag to // make sure of that in the future. -#define MAP_FAILED ((void*)-1L) +#define MAP_FAILED ((void*)(intptr_t)-1L) extern void* mmap(void* start, size_t len, int prot, int flags, int fd, off_t offset); extern int munmap(void* start, size_t len); diff --git a/source/lib/sysdep/win/wposix/wposix.cpp b/source/lib/sysdep/win/wposix/wposix.cpp index 7120de1bb6..988f73bde7 100644 --- a/source/lib/sysdep/win/wposix/wposix.cpp +++ b/source/lib/sysdep/win/wposix/wposix.cpp @@ -34,9 +34,8 @@ static void InitSysconf() // import GlobalMemoryStatusEx - it's not defined by the VC6 PSDK. // used by _SC_*_PAGES if available (provides better results). - const HMODULE hKernel32Dll = LoadLibrary("kernel32.dll"); + const HMODULE hKernel32Dll = GetModuleHandle("kernel32.dll"); *(void**)&pGlobalMemoryStatusEx = GetProcAddress(hKernel32Dll, "GlobalMemoryStatusEx"); - FreeLibrary(hKernel32Dll); } long sysconf(int name) diff --git a/source/lib/sysdep/win/wposix/wtime.h b/source/lib/sysdep/win/wposix/wtime.h index 2ebe9a7893..c6db98799b 100644 --- a/source/lib/sysdep/win/wposix/wtime.h +++ b/source/lib/sysdep/win/wposix/wtime.h @@ -11,13 +11,6 @@ #ifndef INCLUDED_WTIME #define INCLUDED_WTIME -// advertise support for the timer routines we emulate; used by timer.cpp. -// #undef to avoid macro redefinition warning. -#undef HAVE_CLOCK_GETTIME -#define HAVE_CLOCK_GETTIME 1 -#undef HAVE_GETTIMEOFDAY -#define HAVE_GETTIMEOFDAY 1 - // // diff --git a/source/lib/sysdep/win/wposix/wutsname.cpp b/source/lib/sysdep/win/wposix/wutsname.cpp index 6f8bc00876..4bebc3b8c3 100644 --- a/source/lib/sysdep/win/wposix/wutsname.cpp +++ b/source/lib/sysdep/win/wposix/wutsname.cpp @@ -32,7 +32,7 @@ int uname(struct utsname* un) if(ok) SetLastError(last_err); else - debug_warn("GetComputerName failed"); + debug_assert(0); // GetComputerName failed // hardware type static SYSTEM_INFO si; diff --git a/source/lib/sysdep/win/wprintf.cpp b/source/lib/sysdep/win/wprintf.cpp index ac87709761..55a266839c 100644 --- a/source/lib/sysdep/win/wprintf.cpp +++ b/source/lib/sysdep/win/wprintf.cpp @@ -285,7 +285,7 @@ int sys_vsnprintf(TCHAR* buffer, size_t count, const TCHAR* format, va_list argp if (chr == _T('I')) { - debug_warn("MSVC-style \"%I64\" is not allowed!"); + debug_assert(0); // MSVC-style \"%I64\" is not allowed! } if (is_lengthmod(chr)) @@ -413,7 +413,7 @@ finished_reading: */ // Because of those dangerous assumptions about varargs: -#if !CPU_IA32 +#if !ARCH_IA32 #error SLIGHTLY FATAL ERROR: Only x86 is supported! #endif @@ -434,7 +434,7 @@ finished_reading: { if (varsizes[i] <= 0) { - debug_warn("Invalid variable type somewhere - make sure all variable things are positional and defined"); + debug_assert(0); // Invalid variable type somewhere - make sure all variable things are positional and defined return -1; } @@ -456,7 +456,7 @@ finished_reading: FormatVariable* s = static_cast(*it); if (s->position <= 0) { - debug_warn("Invalid use of positional elements - make sure all variable things are positional and defined"); + debug_assert(0); // Invalid use of positional elements - make sure all variable things are positional and defined return -1; } newstack += std::string( stackitems[s->position-1].first, stackitems[s->position-1].second ); diff --git a/source/lib/sysdep/win/wprofiler.cpp b/source/lib/sysdep/win/wprofiler.cpp index 79243ca5ee..ab1f2e0f7b 100644 --- a/source/lib/sysdep/win/wprofiler.cpp +++ b/source/lib/sysdep/win/wprofiler.cpp @@ -53,7 +53,7 @@ static uintptr_t get_target_pc() ret = SuspendThread(hThread); if(ret == (DWORD)-1) { - debug_warn("get_target_pc: SuspendThread failed"); + debug_assert(0); // get_target_pc: SuspendThread failed return 0; } // note: we don't need to call more than once: this increments a DWORD @@ -114,7 +114,7 @@ static void* prof_thread_func(void* UNUSED(data)) break; // actual error: warn if(errno != ETIMEDOUT) - debug_warn("wpcu prof_thread_func: sem_timedwait failed"); + debug_assert(0); // wpcu prof_thread_func: sem_timedwait failed uintptr_t pc = get_target_pc(); UNUSED2(pc); diff --git a/source/lib/sysdep/win/wsdl.cpp b/source/lib/sysdep/win/wsdl.cpp index e52af23263..ec9d4e8266 100644 --- a/source/lib/sysdep/win/wsdl.cpp +++ b/source/lib/sysdep/win/wsdl.cpp @@ -17,7 +17,6 @@ #include #include "win.h" -#include #include // _beginthreadex #include // message crackers @@ -28,7 +27,7 @@ #include "winit.h" // for easy removal of DirectDraw dependency (used to query total video mem) -#define DDRAW +//#define DDRAW #if MSC_VERSION #pragma comment(lib, "user32.lib") @@ -37,6 +36,7 @@ // don't bother with dynamic linking - // DirectX is present in all Windows versions since Win95. #ifdef DDRAW +#include # pragma comment(lib, "ddraw.lib") #endif @@ -234,7 +234,7 @@ int SDL_SetVideoMode(int w, int h, int bpp, unsigned long flags) ATOM class_atom = RegisterClass(&wc); if(!class_atom) { - debug_warn("SDL_SetVideoMode: RegisterClass failed"); + debug_assert(0); // SDL_SetVideoMode: RegisterClass failed return 0; } @@ -319,8 +319,10 @@ fail: static void video_shutdown() { if(fullscreen) - if(ChangeDisplaySettings(0, 0) != DISP_CHANGE_SUCCESSFUL) - debug_warn("ChangeDisplaySettings failed"); + { + LONG status = ChangeDisplaySettings(0, 0); + debug_assert(status == DISP_CHANGE_SUCCESSFUL); + } if(hGLRC != INVALID_HANDLE_VALUE) { @@ -653,7 +655,7 @@ inline SDLKey vkmap(int vk) if(!(0 <= vk && vk < 256)) { - debug_warn("vkmap: invalid vk"); + debug_assert(0); // invalid vk return SDLK_UNKNOWN; } return VK_SDLKMap[vk]; diff --git a/source/lib/sysdep/win/wseh.cpp b/source/lib/sysdep/win/wseh.cpp index 85b1902481..a75be32d99 100644 --- a/source/lib/sysdep/win/wseh.cpp +++ b/source/lib/sysdep/win/wseh.cpp @@ -330,6 +330,8 @@ C++ classes. this way is more reliable/documented, but has several drawbacks: */ +#ifndef LIB_DLL + EXTERN_C int mainCRTStartup(); static int CallStartupWithinTryBlock() @@ -366,3 +368,5 @@ EXTERN_C int wseh_EntryPoint() #endif return CallStartupWithinTryBlock(); } + +#endif diff --git a/source/lib/sysdep/win/wsnd.cpp b/source/lib/sysdep/win/wsnd.cpp index bbfe376bce..3ad8b7947d 100644 --- a/source/lib/sysdep/win/wsnd.cpp +++ b/source/lib/sysdep/win/wsnd.cpp @@ -127,8 +127,8 @@ static const char* GetDirectSoundDriverPath() *(void**)&pDirectSoundEnumerateA = GetProcAddress(hDsoundDll, "DirectSoundEnumerateA"); if(pDirectSoundEnumerateA) { - if(pDirectSoundEnumerateA(DirectSoundCallback, (void*)0) != DS_OK) - debug_warn("DirectSoundEnumerate failed"); + HRESULT ret = pDirectSoundEnumerateA(DirectSoundCallback, (void*)0); + debug_assert(ret == DS_OK); } FreeLibrary(hDsoundDll); diff --git a/source/lib/sysdep/win/wsysdep.cpp b/source/lib/sysdep/win/wsysdep.cpp index de3bb48e35..9416b4c31c 100644 --- a/source/lib/sysdep/win/wsysdep.cpp +++ b/source/lib/sysdep/win/wsysdep.cpp @@ -249,7 +249,7 @@ ErrorReaction sys_display_error(const wchar_t* text, uint flags) MSG msg; BOOL quit_pending = PeekMessage(&msg, 0, WM_QUIT, WM_QUIT, PM_REMOVE); - const HINSTANCE hInstance = GetModuleHandle(0); + const HINSTANCE hInstance = wutil_LibModuleHandle; LPCSTR lpTemplateName = MAKEINTRESOURCE(IDD_DIALOG1); const DialogParams params = { text, flags }; // get the enclosing app's window handle. we can't just pass 0 or diff --git a/source/lib/sysdep/win/wutil.cpp b/source/lib/sysdep/win/wutil.cpp index abe8a0cf21..0587274827 100644 --- a/source/lib/sysdep/win/wutil.cpp +++ b/source/lib/sysdep/win/wutil.cpp @@ -105,7 +105,7 @@ static void ShutdownLocks() // only call after a Win32 function indicates failure. LibError LibError_from_GLE(bool warn_if_failed) { - LibError err; + LibError err = ERR::FAIL; switch(GetLastError()) { case ERROR_OUTOFMEMORY: @@ -123,8 +123,6 @@ LibError LibError_from_GLE(bool warn_if_failed) case ERROR_PATH_NOT_FOUND: err = ERR::TNODE_NOT_FOUND; break; */ - default: - err = ERR::FAIL; break; } if(warn_if_failed) @@ -285,7 +283,7 @@ static void EnableMemoryTracking() static void EnableLowFragmentationHeap() { #if WINVER >= 0x0501 - HMODULE hKernel32Dll = LoadLibrary("kernel32.dll"); + const HMODULE hKernel32Dll = GetModuleHandle("kernel32.dll"); BOOL (WINAPI* pHeapSetInformation)(HANDLE, HEAP_INFORMATION_CLASS, void*, size_t); *(void**)&pHeapSetInformation = GetProcAddress(hKernel32Dll, "HeapSetInformation"); if(!pHeapSetInformation) @@ -293,8 +291,6 @@ static void EnableLowFragmentationHeap() ULONG flags = 2; // enable LFH pHeapSetInformation(GetProcessHeap(), HeapCompatibilityInformation, &flags, sizeof(flags)); - - FreeLibrary(hKernel32Dll); #endif // #if WINVER >= 0x0501 } @@ -376,11 +372,10 @@ static bool isWow64; static void ImportWow64Functions() { - HMODULE hKernel32Dll = LoadLibrary("kernel32.dll"); + const HMODULE hKernel32Dll = GetModuleHandle("kernel32.dll"); *(void**)&pIsWow64Process = GetProcAddress(hKernel32Dll, "IsWow64Process"); *(void**)&pWow64DisableWow64FsRedirection = GetProcAddress(hKernel32Dll, "Wow64DisableWow64FsRedirection"); *(void**)&pWow64RevertWow64FsRedirection = GetProcAddress(hKernel32Dll, "Wow64RevertWow64FsRedirection"); - FreeLibrary(hKernel32Dll); } static void DetectWow64() @@ -424,6 +419,27 @@ void wutil_RevertWow64Redirection(void* wasRedirectionEnabled) } +//----------------------------------------------------------------------------- +// module handle + +#ifdef LIB_DLL + +HMODULE wutil_LibModuleHandle; + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD reason, LPVOID reserved) +{ + DisableThreadLibraryCalls(hInstance); + wutil_LibModuleHandle = hInstance; + return TRUE; // success (ignored unless reason == DLL_PROCESS_ATTACH) +} + +#else + +HMODULE wutil_LibModuleHandle = GetModuleHandle(0); + +#endif + + //----------------------------------------------------------------------------- // find main window diff --git a/source/lib/sysdep/win/wutil.h b/source/lib/sysdep/win/wutil.h index 290e05fdbd..a28945eb00 100644 --- a/source/lib/sysdep/win/wutil.h +++ b/source/lib/sysdep/win/wutil.h @@ -60,6 +60,19 @@ extern int win_is_locked(uint idx); win_unlock(ONCE_CS);\ } +struct WinScopedLock +{ + WinScopedLock() + { + win_lock(WAIO_CS); + } + + ~WinScopedLock() + { + win_unlock(WAIO_CS); + } +}; + // // error codes @@ -133,6 +146,14 @@ extern void wutil_DisableWow64Redirection(void*& wasRedirectionEnabled); extern void wutil_RevertWow64Redirection(void* wasRedirectionEnabled); +/** + * module handle of lib code (that of the main EXE if linked statically, + * otherwise the DLL). + * this is necessary for the error dialog. + **/ +extern HMODULE wutil_LibModuleHandle; + + /** * @return handle to the first window owned by the current process, or * 0 if none exist (e.g. it hasn't yet created one). diff --git a/source/lib/timer.cpp b/source/lib/timer.cpp index c1cf093fc7..ea819c0f78 100644 --- a/source/lib/timer.cpp +++ b/source/lib/timer.cpp @@ -24,19 +24,34 @@ #if OS_WIN #include "lib/sysdep/win/whrt/whrt.h" #endif - -#if CONFIG_TIMER_ALLOW_RDTSC +#if OS_UNIX +# include +#endif +#if ARCH_IA32 && CONFIG_TIMER_ALLOW_RDTSC # include "lib/sysdep/ia32/ia32.h" // ia32_rdtsc #endif -// rationale for wrapping gettimeofday and clock_gettime, instead of emulating -// them where not available: allows returning higher-resolution timer values -// than their us / ns interface, via double [seconds]. they're also not -// guaranteed to be monotonic. -#if HAVE_CLOCK_GETTIME +#if OS_UNIX || OS_WIN +# define HAVE_GETTIMEOFDAY 1 +#else +# define HAVE_GETTIMEOFDAY 0 +#endif + +#if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) || OS_WIN +# define HAVE_CLOCK_GETTIME 1 +#else +# define HAVE_CLOCK_GETTIME 0 +#endif + +// rationale for wrapping gettimeofday and clock_gettime, instead of just +// emulating them where not available: allows returning higher-resolution +// timer values than their us / ns interface, via double [seconds]. +// they're also not guaranteed to be monotonic. + +#if HAVE_GETTIMEOFDAY static struct timespec start; -#elif HAVE_GETTIMEOFDAY +#elif HAVE_CLOCK_GETTIME static struct timeval start; #endif @@ -107,141 +122,6 @@ double timer_res() } -// calculate fps (call once per frame). -// algorithm: variable-gain IIR filter. -// less fluctuation, but rapid tracking. -// filter values are tuned for 100 FPS. - -int fps; -float spf; - -void calc_fps() -{ - static double avg_fps = 60.0; - double cur_fps = avg_fps; - - // get elapsed time [s] since last update - static double last_t; - const double t = get_time(); - ONCE(last_t = t - 1.0/60.0); // first call: 60 FPS - const double dt = t - last_t; - - // (in case timer resolution is low): count frames until - // timer value has changed "enough". - static double min_dt; - ONCE(min_dt = timer_res() * 4.0); - // chosen to reduce error but still yield rapid updates. - static uint num_frames = 1; - if(dt < min_dt) - { - num_frames++; - return; - } - - // dt is big enough => we will update. - // calculate approximate current FPS (= 1 / elapsed time per frame). - last_t = t; - cur_fps = (1.0 / dt) * num_frames; - num_frames = 1; // reset for next time - - - // average and smooth cur_fps. - // - // filter design goals: steady output, but rapid signal tracking. - // - // implemented as a variable-gain IIR filter with knowledge of typical - // function characteristics. this is easier to stabilize than a PID - // scheme, since it is based on averaging actual function values, - // instead of trying to minimize output-vs-input error. - // there are some similarities, though: same_side ~= I, and - // bounced ~= D. - - // - // check cur_fps function for several characteristics that - // help decide if it's actually changing or just jittering. - // - -#define REL_ERR(correct, measured) (fabs((correct) - (measured)) / (correct)) -#define SIGN_EQ(x0, x1, x2) ( ((x0) * (x1)) > 0.0 && ((x1) * (x2)) > 0.0 ) -#define ONE_SIDE(x, x0, x1, x2) SIGN_EQ(x-x0, x-x1, x-x2) - - // cur_fps history and changes over past few frames - static double h2, h1 = 30.0, h0 = 30.0; - h2 = h1; h1 = h0; h0 = cur_fps; - const double d21 = h1 - h2, d10 = h0 - h1; - const double e20 = REL_ERR(h2, h0), e10 = REL_ERR(h1, h0); - const double e0 = REL_ERR(avg_fps, h0); - - // indicators that the function is jittering - const bool bounced = d21 * d10 < 0.0 && e20 < 0.05 && e10 > 0.10; - // /\ or \/ - const bool jumped = e10 > 0.30; - // large change (have seen semi-legitimate changes of 25%) - const bool is_close = e0 < 0.02; - // cur_fps - avg_fps is "small" - - // "same-side" check for rapid tracking of the function. - // if the past few samples have been consistently above/below the average, - // the function is moving up/down and we need to catch up. - static int same_side; - // consecutive times the last 3 samples have been on the same side. - if(!ONE_SIDE(avg_fps, h0, h1, h2)) // not all on same side: - same_side = 0; // reset counter - // (only increase if not too close to average, - // so that this isn't triggered by jitter alone) - if(!is_close) - same_side++; - - - // - // determine filter gain, based on above characteristics. - // - - static double gain; // sensitivity to changes in cur_fps ([0,1]) - double bias = 0.0; // (unlimited) exponential change to gain - - // ignore (gain -> 0) large jumps. - if(jumped) - bias -= 4.0; - // don't let a "bounce" affect things too much. - else if(bounced) - bias -= 1.0; - // otherwise, function is normal here. - else - { - // function is changing, we need to track it rapidly. - // note: check close again so we aren't too loose if the function - // comes closer to the average again (meaning it probably - // wasn't really changing). - if(same_side >= 2 && !is_close) - bias += std::min(same_side, 4); - } - - // bias = 0: no change. > 0: increase (n-th root). < 0: decrease (^n) - double e = (bias > 0)? 1.0 / bias : -bias; - if(e == 0.0) e = 1.0; - gain = pow(0.08, e); - // default: fairly insensitive to changes (~= 16 sample average) - - - // IIR filter - static double old = 30.0; - old = cur_fps*gain + old*(1.0-gain); - avg_fps = old; - - spf = 1.0 / avg_fps; - - // update fps counter if it differs "enough" - // currently, that means off by more than 5 FPS or 5%. - const double difference = fabs(avg_fps-fps); - const double threshold = fminf(5.f, 0.05f*fps); - if(difference > threshold) - fps = (int)(avg_fps + 0.99); - // C float -> int rounds down; we want to round up to - // hit vsync-locked framerates exactly. -} - - //----------------------------------------------------------------------------- // cumulative timer API, useful for profiling. @@ -304,17 +184,7 @@ void timer_display_client_totals() clients = tc->next; num_clients--; - // convert raw ticks into seconds, if necessary - double sum; -#if TIMER_USE_RAW_TICKS -# if CPU_IA32 - sum = tc->sum / cpu_ClockFrequency(); -# else -# error "port" -# endif -#else - sum = tc->sum; -#endif + const double sum = Timer::ToSeconds(tc->sum); // determine scale factor for pretty display double scale = 1e6; @@ -331,7 +201,7 @@ void timer_display_client_totals() } -#if CONFIG_TIMER_ALLOW_RDTSC +#if ARCH_IA32 && CONFIG_TIMER_ALLOW_RDTSC TimerRdtsc::unit TimerRdtsc::get_timestamp() const { diff --git a/source/lib/timer.h b/source/lib/timer.h index 3de30ed417..5dba6b8d74 100644 --- a/source/lib/timer.h +++ b/source/lib/timer.h @@ -15,26 +15,18 @@ #include #include "debug.h" // debug_printf +#include "lib/sysdep/cpu.h" -extern void timer_Init(); -extern void timer_Shutdown(); +LIB_API void timer_Init(); +LIB_API void timer_Shutdown(); // high resolution (> 1 us) timestamp [s], starting at or near 0 s. -extern double get_time(void); +LIB_API double get_time(void); // return resolution (expressed in [s]) of the time source underlying // get_time. -extern double timer_res(void); - -// calculate fps (call once per frame) -// several smooth filters (tuned for ~100 FPS) -// => less fluctuation, but rapid tracking - -extern int fps; // for user display -extern float spf; // for time-since-last-frame use - -extern void calc_fps(void); +LIB_API double timer_res(void); //----------------------------------------------------------------------------- @@ -57,7 +49,7 @@ extern void calc_fps(void); // note that overflow isn't an issue either way (63 bit cycle counts // at 10 GHz cover intervals of 29 years). -#if CONFIG_TIMER_ALLOW_RDTSC +#if ARCH_IA32 && CONFIG_TIMER_ALLOW_RDTSC // fast, IA-32 specific, not usable as wall-clock // (see http://www.gamedev.net/reference/programming/features/timing) @@ -66,6 +58,10 @@ class TimerRdtsc public: typedef i64 unit; unit get_timestamp() const; + static double ToSeconds(unit value) + { + return value / cpu_ClockFrequency(); + } }; #endif @@ -78,9 +74,13 @@ public: { return get_time(); } + static double ToSeconds(unit value) + { + return value; + } }; -#if CONFIG_TIMER_ALLOW_RDTSC +#if ARCH_IA32 && CONFIG_TIMER_ALLOW_RDTSC typedef TimerRdtsc Timer; #else typedef TimerSafe Timer; @@ -122,14 +122,14 @@ struct TimerClient // - always succeeds (there's no fixed limit); // - free() is not needed nor possible. // - description must remain valid until exit; a string literal is safest. -extern TimerClient* timer_add_client(TimerClient* tc, const char* description); +LIB_API TimerClient* timer_add_client(TimerClient* tc, const char* description); // add
to the client's total. -extern void timer_bill_client(TimerClient* tc, TimerUnit dt); +LIB_API void timer_bill_client(TimerClient* tc, TimerUnit dt); // display all clients' totals; does not reset them. // typically called at exit. -extern void timer_display_client_totals(); +LIB_API void timer_display_client_totals(); //-----------------------------------------------------------------------------