summaryrefslogtreecommitdiff
path: root/src/debug
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug')
-rw-r--r--src/debug/_nvwa.h112
-rw-r--r--src/debug/c++11.h340
-rw-r--r--src/debug/debug_new.cpp890
-rw-r--r--src/debug/debug_new.h191
-rw-r--r--src/debug/fast_mutex.h490
-rw-r--r--src/debug/static_assert.h35
6 files changed, 1476 insertions, 582 deletions
diff --git a/src/debug/_nvwa.h b/src/debug/_nvwa.h
new file mode 100644
index 000000000..096893c59
--- /dev/null
+++ b/src/debug/_nvwa.h
@@ -0,0 +1,112 @@
+// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+// vim:tabstop=4:shiftwidth=4:expandtab:
+
+/*
+ * Copyright (C) 2013-2015 Wu Yongwei <adah at users dot sourceforge dot net>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any
+ * damages arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute
+ * it freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgement in the product
+ * documentation would be appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ * This file is part of Stones of Nvwa:
+ * http://sourceforge.net/projects/nvwa
+ *
+ */
+
+/**
+ * @file _nvwa.h
+ *
+ * Common definitions for preprocessing.
+ *
+ * @date 2015-10-28
+ */
+
+#ifndef NVWA_NVWA_H
+#define NVWA_NVWA_H
+
+/**
+ * @namespace nvwa
+ * Namespace of the nvwa project. Most functions and global variables
+ * are defined in this namespace.
+ */
+
+#ifndef NVWA_USE_NAMESPACE
+#ifdef __cplusplus
+#define NVWA_USE_NAMESPACE 1
+#else
+#define NVWA_USE_NAMESPACE 0
+#endif // __cplusplus
+#endif // NVWA_USE_NAMESPACE
+
+#if NVWA_USE_NAMESPACE
+#define NVWA_NAMESPACE_BEGIN namespace nvwa {
+#define NVWA_NAMESPACE_END }
+#define NVWA nvwa
+#else // NVWA_USE_NAMESPACE
+#define NVWA_NAMESPACE_BEGIN
+#define NVWA_NAMESPACE_END
+#define NVWA
+#endif // NVWA_USE_NAMESPACE
+
+#ifndef NVWA_APPLE
+#if defined(__APPLE__) && defined(__MACH__)
+#define NVWA_APPLE 1
+#else
+#define NVWA_APPLE 0
+#endif
+#endif // NVWA_APPLE
+
+#ifndef NVWA_CYGWIN
+#if defined(__CYGWIN__)
+#define NVWA_CYGWIN 1
+#else
+#define NVWA_CYGWIN 0
+#endif
+#endif // NVWA_CYGWIN
+
+#ifndef NVWA_LINUX
+#if defined(__linux__) || defined(__linux)
+#define NVWA_LINUX 1
+#else
+#define NVWA_LINUX 0
+#endif
+#endif // NVWA_LINUX
+
+#ifndef NVWA_UNIX
+#if defined(__unix__) || defined(__unix) || NVWA_APPLE
+#define NVWA_UNIX 1
+#else
+#define NVWA_UNIX 0
+#endif
+#endif // NVWA_UNIX
+
+#ifndef NVWA_WIN32
+#if defined(_WIN32)
+#define NVWA_WIN32 1
+#else
+#define NVWA_WIN32 0
+#endif
+#endif // NVWA_WIN32
+
+#ifndef NVWA_WINDOWS
+#if NVWA_CYGWIN || NVWA_WIN32
+#define NVWA_WINDOWS 1
+#else
+#define NVWA_WINDOWS 0
+#endif
+#endif // NVWA_WINDOWS
+
+#endif // NVWA_NVWA_H
diff --git a/src/debug/c++11.h b/src/debug/c++11.h
new file mode 100644
index 000000000..8d8e2d8af
--- /dev/null
+++ b/src/debug/c++11.h
@@ -0,0 +1,340 @@
+// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+// vim:tabstop=4:shiftwidth=4:expandtab:
+
+/*
+ * Copyright (C) 2013-2017 Wu Yongwei <adah at users dot sourceforge dot net>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any
+ * damages arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute
+ * it freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgement in the product
+ * documentation would be appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ * This file is part of Stones of Nvwa:
+ * http://sourceforge.net/projects/nvwa
+ *
+ */
+
+/**
+ * @file c++11.h
+ *
+ * C++11 feature detection macros and workarounds.
+ *
+ * @date 2017-04-03
+ */
+
+#ifndef NVWA_CXX11_H
+#define NVWA_CXX11_H
+
+// Only Clang provides these macros; they need to be defined as follows
+// to get a valid expression in preprocessing by other compilers.
+#ifndef __has_extension
+#define __has_extension(x) 0
+#endif
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+#ifndef __has_include
+#define __has_include(x) 0
+#endif
+
+// Detect whether C++11 mode is on (for GCC and Clang). MSVC does not
+// have a special C++11 mode, so it is always on for Visual C++ 2010 and
+// later.
+#if __cplusplus >= 201103L || \
+ defined(__GXX_EXPERIMENTAL_CXX0X__) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1600)
+#define NVWA_CXX11_MODE 1
+#else
+#define NVWA_CXX11_MODE 0
+#endif
+
+
+/* Feature checks */
+
+#if !defined(HAVE_CXX11_ATOMIC)
+#if NVWA_CXX11_MODE && \
+ ((__has_include(<atomic>) && !defined(__MINGW32__)) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1700) || \
+ (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405) || \
+ defined(__clang__)) && \
+ (!defined(__MINGW32__) || defined(_POSIX_THREADS))))
+// Note: MinGW GCC, unless built with POSIX threads (as in
+// MinGW-builds), does not support atomics as of 4.8.
+#define HAVE_CXX11_ATOMIC 1
+#else
+#define HAVE_CXX11_ATOMIC 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_AUTO_TYPE)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_auto_type) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1600) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404))
+#define HAVE_CXX11_AUTO_TYPE 1
+#else
+#define HAVE_CXX11_AUTO_TYPE 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_DELETED_FUNCTION)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_deleted_functions) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1800) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404))
+#define HAVE_CXX11_DELETED_FUNCTION 1
+#else
+#define HAVE_CXX11_DELETED_FUNCTION 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_EXPLICIT_CONVERSION)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_explicit_conversions) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1900) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405))
+#define HAVE_CXX11_EXPLICIT_CONVERSION 1
+#else
+#define HAVE_CXX11_EXPLICIT_CONVERSION 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_FINAL)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_override_control) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1700) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 407))
+#define HAVE_CXX11_FINAL 1
+#else
+#define HAVE_CXX11_FINAL 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_FUTURE)
+#if NVWA_CXX11_MODE && \
+ ((__has_include(<future>) && !defined(__MINGW32__)) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1700) || \
+ (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405) || \
+ defined(__clang__)) && \
+ (!defined(__MINGW32__) || defined(_POSIX_THREADS))))
+// Note: MinGW GCC, unless built with POSIX threads (as in
+// MinGW-builds), does not support futures as of 4.8.
+#define HAVE_CXX11_FUTURE 1
+#else
+#define HAVE_CXX11_FUTURE 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_GENERALIZED_INITIALIZER)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_generalized_initializers) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1800) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404))
+#define HAVE_CXX11_GENERALIZED_INITIALIZER 1
+#else
+#define HAVE_CXX11_GENERALIZED_INITIALIZER 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_LAMBDA)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_lambdas) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1600) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405))
+#define HAVE_CXX11_LAMBDA 1
+#else
+#define HAVE_CXX11_LAMBDA 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_MUTEX)
+#if NVWA_CXX11_MODE && \
+ ((__has_include(<mutex>) && !defined(__MINGW32__)) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1700) || \
+ (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 403) || \
+ defined(__clang__)) && \
+ (!defined(__MINGW32__) || defined(_POSIX_THREADS))))
+// Note: MinGW GCC, unless built with POSIX threads (as in
+// MinGW-builds), does not support std::mutex as of 4.8.
+#define HAVE_CXX11_MUTEX 1
+#else
+#define HAVE_CXX11_MUTEX 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_NOEXCEPT)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_noexcept) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1900) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406))
+#define HAVE_CXX11_NOEXCEPT 1
+#else
+#define HAVE_CXX11_NOEXCEPT 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_NULLPTR)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_nullptr) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1600) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406))
+#define HAVE_CXX11_NULLPTR 1
+#else
+#define HAVE_CXX11_NULLPTR 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_OVERRIDE)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_override_control) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1600) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 407))
+#define HAVE_CXX11_OVERRIDE 1
+#else
+#define HAVE_CXX11_OVERRIDE 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_RANGE_FOR)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_range_for) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1700) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406))
+#define HAVE_CXX11_RANGE_FOR 1
+#else
+#define HAVE_CXX11_RANGE_FOR 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_RVALUE_REFERENCE)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_rvalue_references) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1600) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405))
+#define HAVE_CXX11_RVALUE_REFERENCE 1
+#else
+#define HAVE_CXX11_RVALUE_REFERENCE 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_STATIC_ASSERT)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_static_assert) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1600) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 403))
+#define HAVE_CXX11_STATIC_ASSERT 1
+#else
+#define HAVE_CXX11_STATIC_ASSERT 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_THREAD)
+#if NVWA_CXX11_MODE && \
+ ((__has_include(<thread>) && !defined(__MINGW32__)) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1700) || \
+ (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404) || \
+ defined(__clang__)) && \
+ (!defined(__MINGW32__) || defined(_POSIX_THREADS))))
+// Note: MinGW GCC, unless built with POSIX threads (as in
+// MinGW-builds), does not support std::thread as of 4.8.
+#define HAVE_CXX11_THREAD 1
+#else
+#define HAVE_CXX11_THREAD 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_THREAD_LOCAL)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_thread_local) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1900) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 408))
+#define HAVE_CXX11_THREAD_LOCAL 1
+#else
+#define HAVE_CXX11_THREAD_LOCAL 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_TYPE_TRAITS)
+#if NVWA_CXX11_MODE && \
+ (__has_include(<type_traits>) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1600) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 403))
+#define HAVE_CXX11_TYPE_TRAITS 1
+#else
+#define HAVE_CXX11_TYPE_TRAITS 0
+#endif
+#endif
+
+#if !defined(HAVE_CXX11_UNICODE_LITERAL)
+#if NVWA_CXX11_MODE && \
+ (__has_feature(cxx_unicode_literals) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1900) || \
+ (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405))
+#define HAVE_CXX11_UNICODE_LITERAL 1
+#else
+#define HAVE_CXX11_UNICODE_LITERAL 0
+#endif
+#endif
+
+
+/* Workarounds */
+
+#if HAVE_CXX11_DELETED_FUNCTION
+#define _DELETED = delete
+#else
+#define _DELETED
+#endif
+
+#if HAVE_CXX11_FINAL
+#define _FINAL final
+#else
+#define _FINAL
+#endif
+
+#if HAVE_CXX11_OVERRIDE
+#define _OVERRIDE override
+#else
+#define _OVERRIDE
+#endif
+
+#if HAVE_CXX11_NOEXCEPT
+#define _NOEXCEPT noexcept
+#define _NOEXCEPT_(x) noexcept(x)
+#else
+#ifdef _MSC_VER
+#define _NOEXCEPT throw ()
+#else
+#define _NOEXCEPT throw()
+#endif
+#define _NOEXCEPT_(x)
+#endif
+
+#if HAVE_CXX11_NULLPTR
+#define _NULLPTR nullptr
+#else
+#define _NULLPTR NULL
+#endif
+
+#if HAVE_CXX11_THREAD_LOCAL
+#define _THREAD_LOCAL thread_local
+#else
+#ifdef _MSC_VER
+#define _THREAD_LOCAL __declspec(thread)
+#else
+#define _THREAD_LOCAL __thread
+#endif
+#endif
+
+#endif // NVWA_CXX11_H
diff --git a/src/debug/debug_new.cpp b/src/debug/debug_new.cpp
index 7119ece2a..0daa66fe1 100644
--- a/src/debug/debug_new.cpp
+++ b/src/debug/debug_new.cpp
@@ -2,7 +2,7 @@
// vim:tabstop=4:shiftwidth=4:expandtab:
/*
- * Copyright (C) 2004-2008 Wu Yongwei <adah at users dot sourceforge dot net>
+ * Copyright (C) 2004-2016 Wu Yongwei <adah at users dot sourceforge dot net>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
@@ -24,236 +24,283 @@
* This file is part of Stones of Nvwa:
* http://sourceforge.net/projects/nvwa
*
- * original version changed for ManaPlus
- *
- * Copyright (C) 2011-2017 The ManaPlus Developers
*/
/**
- * @file debug_new.cpp
+ * @file debug_new.cpp
*
* Implementation of debug versions of new and delete to check leakage.
*
- * @version 4.14, 2008/10/20
- * @author Wu Yongwei
- *
+ * @date 2016-10-14
*/
-#ifdef ENABLE_MEM_DEBUG
-#include <new>
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef __unix__
-#include <alloca.h>
+#include <new> // std::bad_alloc/nothrow_t
+#include <assert.h> // assert
+#include <stdio.h> // fprintf/stderr
+#include <stdlib.h> // abort
+#include <string.h> // strcpy/strncpy/sprintf
+#include "debug/_nvwa.h" // NVWA macros
+
+#if NVWA_UNIX
+#include <alloca.h> // alloca
#endif
-#ifdef _WIN32
-#include <malloc.h>
+#if NVWA_WIN32
+#include <malloc.h> // alloca
#endif
-#include "debug/fast_mutex.h"
-#include "debug/static_assert.h"
-#include "localconsts.h"
+#if NVWA_LINUX || NVWA_APPLE
+#include <execinfo.h> // backtrace
+#endif
+
+#if NVWA_WINDOWS
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h> // CaptureStackBackTrace
+#endif
-// #define DUMP_MEM_ADDRESSES 1
+#include "debug/c++11.h" // _NOEXCEPT/_NULLPTR
+#include "debug/fast_mutex.h" // nvwa::fast_mutex
+#include "debug/static_assert.h" // STATIC_ASSERT
+
+#undef _DEBUG_NEW_EMULATE_MALLOC
+#undef _DEBUG_NEW_REDEFINE_NEW
+/**
+ * Macro to indicate whether redefinition of \c new is wanted. Here it
+ * is defined to \c 0 to disable the redefinition of \c new.
+ */
+#define _DEBUG_NEW_REDEFINE_NEW 0
+#include "debug/debug_new.h"
#if !_FAST_MUTEX_CHECK_INITIALIZATION && !defined(_NOTHREADS)
#error "_FAST_MUTEX_CHECK_INITIALIZATION not set: check_leaks may not work"
#endif
/**
- * @def M_DEBUG_NEW_ALIGNMENT
+ * @def _DEBUG_NEW_ALIGNMENT
*
* The alignment requirement of allocated memory blocks. It must be a
* power of two.
*/
-#ifndef M_DEBUG_NEW_ALIGNMENT
-#define M_DEBUG_NEW_ALIGNMENT 16
+#ifndef _DEBUG_NEW_ALIGNMENT
+#define _DEBUG_NEW_ALIGNMENT 16
#endif
/**
- * @def M_DEBUG_NEW_CALLER_ADDRESS
+ * @def _DEBUG_NEW_CALLER_ADDRESS
*
- * The expression to return the caller address. #print_position will
+ * The expression to return the caller address. nvwa#print_position will
* later on use this address to print the position information of memory
* operation points.
*/
-#ifndef M_DEBUG_NEW_CALLER_ADDRESS
+#ifndef _DEBUG_NEW_CALLER_ADDRESS
#ifdef __GNUC__
-#define M_DEBUG_NEW_CALLER_ADDRESS __builtin_return_address(0)
+#define _DEBUG_NEW_CALLER_ADDRESS __builtin_return_address(0)
#else
-#define M_DEBUG_NEW_CALLER_ADDRESS NULL
+#define _DEBUG_NEW_CALLER_ADDRESS _NULLPTR
#endif
#endif
/**
- * @def M_DEBUG_NEW_ERROR_ACTION
+ * @def _DEBUG_NEW_ERROR_ACTION
*
* The action to take when an error occurs. The default behaviour is to
- * call \e abort, unless \c M_DEBUG_NEW_ERROR_CRASH is defined, in which
+ * call \e abort, unless \c _DEBUG_NEW_ERROR_CRASH is defined, in which
* case a segmentation fault will be triggered instead (which can be
* useful on platforms like Windows that do not generate a core dump
* when \e abort is called).
*/
-#ifndef M_DEBUG_NEW_ERROR_ACTION
-#ifndef M_DEBUG_NEW_ERROR_CRASH
-#define M_DEBUG_NEW_ERROR_ACTION abort()
+#ifndef _DEBUG_NEW_ERROR_ACTION
+#ifndef _DEBUG_NEW_ERROR_CRASH
+#define _DEBUG_NEW_ERROR_ACTION abort()
#else
-#define M_DEBUG_NEW_ERROR_ACTION do { *((char*)0) = 0; abort(); } while (0)
+#define _DEBUG_NEW_ERROR_ACTION do { *((char*)0) = 0; abort(); } while (0)
#endif
#endif
/**
- * @def M_DEBUG_NEW_FILENAME_LEN
+ * @def _DEBUG_NEW_FILENAME_LEN
*
* The length of file name stored if greater than zero. If it is zero,
* only a const char pointer will be stored. Currently the default
- * behaviour is to copy the file name, because I found that the exit
- * leakage check cannot access the address of the file name sometimes
- * (in my case, a core dump will occur when trying to access the file
- * name in a shared library after a \c SIGINT). The current default
- * value makes the size of new_ptr_list_t 64 on 32-bit platforms.
+ * value is non-zero (thus to copy the file name) on non-Windows
+ * platforms, because I once found that the exit leakage check could not
+ * access the address of the file name on Linux (in my case, a core dump
+ * occurred when check_leaks tried to access the file name in a shared
+ * library after a \c SIGINT). This value makes the size of
+ * new_ptr_list_t \c 64 on non-Windows 32-bit platforms (w/o stack
+ * backtrace).
*/
-#ifndef M_DEBUG_NEW_FILENAME_LEN
-#define M_DEBUG_NEW_FILENAME_LEN 100
+#ifndef _DEBUG_NEW_FILENAME_LEN
+#if NVWA_WINDOWS
+#define _DEBUG_NEW_FILENAME_LEN 0
+#else
+#define _DEBUG_NEW_FILENAME_LEN 44
+#endif
#endif
/**
- * @def M_DEBUG_NEW_PROGNAME
+ * @def _DEBUG_NEW_PROGNAME
*
* The program (executable) name to be set at compile time. It is
- * better to assign the full program path to #new_progname in \e main
+ * better to assign the full program path to nvwa#new_progname in \e main
* (at run time) than to use this (compile-time) macro, but this macro
* serves well as a quick hack. Note also that double quotation marks
* need to be used around the program name, i.e., one should specify a
- * command-line option like <code>-DM_DEBUG_NEW_PROGNAME=\"a.out\"</code>
- * in \e bash, or <code>-DM_DEBUG_NEW_PROGNAME=\"a.exe\"</code> in the
+ * command-line option like <code>-D_DEBUG_NEW_PROGNAME=\\"a.out\"</code>
+ * in \e bash, or <code>-D_DEBUG_NEW_PROGNAME=\\"a.exe\"</code> in the
* Windows command prompt.
*/
-#ifndef M_DEBUG_NEW_PROGNAME
-#define M_DEBUG_NEW_PROGNAME nullptr
+#ifndef _DEBUG_NEW_PROGNAME
+#define _DEBUG_NEW_PROGNAME _NULLPTR
+#endif
+
+/**
+ * @def _DEBUG_NEW_REMEMBER_STACK_TRACE
+ *
+ * Macro to indicate whether stack traces of allocations should be
+ * included in NVWA allocation information and be printed while leaks
+ * are reported. Useful to pinpoint leaks caused by strdup and other
+ * custom allocation functions. It is also very helpful in filtering
+ * out false positives caused by internal STL/C runtime operations. It
+ * is off by default because it is quite memory heavy. Set it to \c 1
+ * to make all allocations have stack trace attached, or set to \c 2 to
+ * make only allocations that lack calling source code information (file
+ * and line) have the stack trace attached.
+ */
+#ifndef _DEBUG_NEW_REMEMBER_STACK_TRACE
+#define _DEBUG_NEW_REMEMBER_STACK_TRACE 0
#endif
/**
- * @def M_DEBUG_NEW_STD_OPER_NEW
+ * @def _DEBUG_NEW_STD_OPER_NEW
*
* Macro to indicate whether the standard-conformant behaviour of
* <code>operator new</code> is wanted. It is on by default now, but
* the user may set it to \c 0 to revert to the old behaviour.
*/
-#ifndef M_DEBUG_NEW_STD_OPER_NEW
-#define M_DEBUG_NEW_STD_OPER_NEW 1
+#ifndef _DEBUG_NEW_STD_OPER_NEW
+#define _DEBUG_NEW_STD_OPER_NEW 1
#endif
/**
- * @def M_DEBUG_NEW_TAILCHECK
+ * @def _DEBUG_NEW_TAILCHECK
*
* Macro to indicate whether a writing-past-end check will be performed.
* Define it to a positive integer as the number of padding bytes at the
* end of a memory block for checking.
*/
-#ifndef M_DEBUG_NEW_TAILCHECK
-#define M_DEBUG_NEW_TAILCHECK 0
+#ifndef _DEBUG_NEW_TAILCHECK
+#define _DEBUG_NEW_TAILCHECK 0
#endif
/**
- * @def M_DEBUG_NEW_TAILCHECK_CHAR
+ * @def _DEBUG_NEW_TAILCHECK_CHAR
*
* Value of the padding bytes at the end of a memory block.
*/
-// #ifndef M_DEBUG_NEW_TAILCHECK_CHAR
-// #define M_DEBUG_NEW_TAILCHECK_CHAR 0xCC
-// #endif
+#ifndef _DEBUG_NEW_TAILCHECK_CHAR
+#define _DEBUG_NEW_TAILCHECK_CHAR 0xCC
+#endif
/**
- * @def M_DEBUG_NEW_USE_ADDR2LINE
+ * @def _DEBUG_NEW_USE_ADDR2LINE
*
* Whether to use \e addr2line to convert a caller address to file/line
* information. Defining it to a non-zero value will enable the
* conversion (automatically done if GCC is detected). Defining it to
* zero will disable the conversion.
*/
-#ifndef M_DEBUG_NEW_USE_ADDR2LINE
+#ifndef _DEBUG_NEW_USE_ADDR2LINE
#ifdef __GNUC__
-#define M_DEBUG_NEW_USE_ADDR2LINE 1
+#define _DEBUG_NEW_USE_ADDR2LINE 1
#else
-#define M_DEBUG_NEW_USE_ADDR2LINE 0
+#define _DEBUG_NEW_USE_ADDR2LINE 0
#endif
#endif
#ifdef _MSC_VER
-#pragma warning(disable: 4073) // #pragma init_seg(lib) used
+#pragma warning(disable: 4074) // #pragma init_seg(compiler) used
#pragma warning(disable: 4290) // C++ exception specification ignored
-#pragma init_seg(lib)
+#if _MSC_VER >= 1400 // Visual Studio 2005 or later
+#pragma warning(disable: 4996) // Use the `unsafe' strncpy
+#endif
+#pragma init_seg(compiler)
#endif
-#undef M_DEBUG_NEW_EMULATE_MALLOC
-#undef M_DEBUG_NEW_REDEFINE_NEW
/**
- * Macro to indicate whether redefinition of \c new is wanted. Here it
- * is defined to \c 0 to disable the redefinition of \c new.
+ * Gets the aligned value of memory block size.
*/
-#define M_DEBUG_NEW_REDEFINE_NEW 0
-#include "debug/debug_new.h"
+#define ALIGN(s) \
+ (((s) + _DEBUG_NEW_ALIGNMENT - 1) & ~(_DEBUG_NEW_ALIGNMENT - 1))
+
+NVWA_NAMESPACE_BEGIN
/**
- * Gets the aligned value of memory block size.
+ * The platform memory alignment. The current value works well in
+ * platforms I have tested: Windows XP, Windows 7 x64, and Mac OS X
+ * Leopard. It may be smaller than the real alignment, but must be
+ * bigger than \c sizeof(size_t) for it work. nvwa#debug_new_recorder
+ * uses it to detect misaligned pointer returned by `<code>new
+ * NonPODType[size]</code>'.
*/
-#define align(s) \
- (((s) + M_DEBUG_NEW_ALIGNMENT - 1) & ~(M_DEBUG_NEW_ALIGNMENT - 1))
+const size_t PLATFORM_MEM_ALIGNMENT = sizeof(size_t) * 2;
/**
* Structure to store the position information where \c new occurs.
*/
struct new_ptr_list_t
{
- new_ptr_list_t* next;
- new_ptr_list_t* prev;
- size_t size;
+ new_ptr_list_t* next; ///< Pointer to the next memory block
+ new_ptr_list_t* prev; ///< Pointer to the previous memory block
+ size_t size; ///< Size of the memory block
union
{
-#if M_DEBUG_NEW_FILENAME_LEN == 0
- const char* file;
+#if _DEBUG_NEW_FILENAME_LEN == 0
+ const char* file; ///< Pointer to the file name of the caller
#else
- char file[M_DEBUG_NEW_FILENAME_LEN];
+ char file[_DEBUG_NEW_FILENAME_LEN]; ///< File name of the caller
#endif
- void* addr;
+ void* addr; ///< Address of the caller to \e new
};
- unsigned line :31;
- unsigned is_array :1;
- unsigned magic;
- unsigned dumped;
+ unsigned line :31; ///< Line number of the caller; or \c 0
+ unsigned is_array:1; ///< Non-zero iff <em>new[]</em> is used
+#if _DEBUG_NEW_REMEMBER_STACK_TRACE
+ void** stacktrace; ///< Pointer to stack trace information
+#endif
+ unsigned magic; ///< Magic number for error detection
};
/**
- * Magic number for error detection.
+ * Definition of the constant magic number used for error detection.
*/
-const unsigned MAGIC = 0x4442474E;
+static const unsigned DEBUG_NEW_MAGIC = 0x4442474E;
/**
* The extra memory allocated by <code>operator new</code>.
*/
-const int ALIGNED_LIST_ITEM_SIZE = align(sizeof(new_ptr_list_t));
+static const int ALIGNED_LIST_ITEM_SIZE = ALIGN(sizeof(new_ptr_list_t));
/**
* List of all new'd pointers.
*/
-static new_ptr_list_t new_ptr_list =
-{
+static new_ptr_list_t new_ptr_list = {
&new_ptr_list,
&new_ptr_list,
0,
{
-#if M_DEBUG_NEW_FILENAME_LEN == 0
- NULL
+#if _DEBUG_NEW_FILENAME_LEN == 0
+ _NULLPTR
#else
""
#endif
},
0,
0,
- MAGIC,
- false
+#if _DEBUG_NEW_REMEMBER_STACK_TRACE
+ _NULLPTR,
+#endif
+ DEBUG_NEW_MAGIC
};
/**
@@ -272,8 +319,8 @@ static fast_mutex new_output_lock;
static size_t total_mem_alloc = 0;
/**
- * Flag to control whether #check_leaks will be automatically called on
- * program exit.
+ * Flag to control whether nvwa#check_leaks will be automatically called
+ * on program exit.
*/
bool new_autocheck_flag = true;
@@ -291,15 +338,28 @@ FILE* new_output_fp = stderr;
/**
* Pointer to the program name. Its initial value is the macro
- * #M_DEBUG_NEW_PROGNAME. You should try to assign the program path to
+ * #_DEBUG_NEW_PROGNAME. You should try to assign the program path to
* it early in your application. Assigning <code>argv[0]</code> to it
* in \e main is one way. If you use \e bash or \e ksh (or similar),
* the following statement is probably what you want:
* `<code>new_progname = getenv("_");</code>'.
*/
-const char* new_progname = M_DEBUG_NEW_PROGNAME;
+const char* new_progname = _DEBUG_NEW_PROGNAME;
-#if M_DEBUG_NEW_USE_ADDR2LINE
+/**
+ * Pointer to the callback used to print the stack backtrace in case of
+ * a memory problem. A null value causes the default stack trace
+ * printing routine to be used.
+ */
+stacktrace_print_callback_t stacktrace_print_callback = _NULLPTR;
+
+/**
+ * Pointer to the callback used to filter out false positives from leak
+ * reports. A null value means the lack of filtering.
+ */
+leak_whitelist_callback_t leak_whitelist_callback = _NULLPTR;
+
+#if _DEBUG_NEW_USE_ADDR2LINE
/**
* Tries printing the position information from an instruction address.
* This is the version that uses \e addr2line.
@@ -311,7 +371,7 @@ const char* new_progname = M_DEBUG_NEW_PROGNAME;
*/
static bool print_position_from_addr(const void* addr)
{
- static const void* last_addr = nullptr;
+ static const void* last_addr = _NULLPTR;
static char last_info[256] = "";
if (addr == last_addr)
{
@@ -322,32 +382,39 @@ static bool print_position_from_addr(const void* addr)
}
if (new_progname)
{
+#if NVWA_APPLE
+ const char addr2line_cmd[] = "atos -o ";
+#else
const char addr2line_cmd[] = "addr2line -e ";
-#if defined(__CYGWIN__) || defined(_WIN32)
+#endif
+
+#if NVWA_WINDOWS
const int exeext_len = 4;
#else
const int exeext_len = 0;
#endif
-#if !defined(__CYGWIN__) && defined(__unix__)
+
+#if NVWA_UNIX && !NVWA_CYGWIN
const char ignore_err[] = " 2>/dev/null";
-#elif defined(__CYGWIN__) || \
- (defined(_WIN32) && defined(WINVER) && WINVER >= 0x0500)
+#elif NVWA_CYGWIN || \
+ (NVWA_WIN32 && defined(WINVER) && WINVER >= 0x0500)
const char ignore_err[] = " 2>nul";
#else
const char ignore_err[] = "";
#endif
- char* cmd = static_cast<char*>(alloca(strlen(new_progname)
- + exeext_len
- + sizeof addr2line_cmd - 1
- + sizeof ignore_err - 1
- + sizeof(void*) * 2
- + 4 /* SP + "0x" + null */));
+ char* cmd = (char*)alloca(strlen(new_progname)
+ + exeext_len
+ + sizeof addr2line_cmd - 1
+ + sizeof ignore_err - 1
+ + sizeof(void*) * 2
+ + 4 /* SP + "0x" + null */);
strcpy(cmd, addr2line_cmd);
strcpy(cmd + sizeof addr2line_cmd - 1, new_progname);
size_t len = strlen(cmd);
-#if defined(__CYGWIN__) || defined(_WIN32)
- if (len <= 4 || (strcmp(cmd + len - 4, ".exe") != 0
- && strcmp(cmd + len - 4, ".EXE") != 0))
+#if NVWA_WINDOWS
+ if (len <= 4
+ || (strcmp(cmd + len - 4, ".exe") != 0 &&
+ strcmp(cmd + len - 4, ".EXE") != 0))
{
strcpy(cmd + len, ".exe");
len += 4;
@@ -397,11 +464,11 @@ static bool print_position_from_addr(const void*)
{
return false;
}
-#endif // M_DEBUG_NEW_USE_ADDR2LINE
+#endif // _DEBUG_NEW_USE_ADDR2LINE
/**
* Prints the position information of a memory operation point. When \c
- * M_DEBUG_NEW_USE_ADDR2LINE is defined to a non-zero value, this
+ * _DEBUG_NEW_USE_ADDR2LINE is defined to a non-zero value, this
* function will try to convert a given caller address to file/line
* information with \e addr2line.
*
@@ -412,39 +479,89 @@ static bool print_position_from_addr(const void*)
*/
static void print_position(const void* ptr, int line)
{
- if (line != 0) // Is file/line information present?
+ if (line != 0) // Is file/line information present?
{
- fprintf(new_output_fp, "%s:%d", static_cast<const char*>(ptr), line);
+ fprintf(new_output_fp, "%s:%d", (const char*)ptr, line);
}
- else if (ptr != nullptr) // Is caller address present?
+ else if (ptr != _NULLPTR) // Is caller address present?
{
- if (!print_position_from_addr(ptr)) // Fail to get source position?
+ if (!print_position_from_addr(ptr)) // Fail to get source position?
fprintf(new_output_fp, "%p", ptr);
}
- else // No information is present
+ else // No information is present
{
fprintf(new_output_fp, "<Unknown>");
}
}
-#if M_DEBUG_NEW_TAILCHECK
+#if _DEBUG_NEW_REMEMBER_STACK_TRACE
+/**
+ * Prints the stack backtrace.
+ *
+ * When nvwa#stacktrace_print_callback is not null, it is used for
+ * printing the stacktrace items. Default implementation of call stack
+ * printing is very spartan&mdash;only stack frame pointers are
+ * printed&mdash;but even that output is still useful. Just do address
+ * lookup in LLDB etc.
+ *
+ * @param stacktrace pointer to the stack trace array
+ */
+static void print_stacktrace(void** stacktrace)
+{
+ if (stacktrace_print_callback == _NULLPTR)
+ {
+ fprintf(new_output_fp, "Stack backtrace:\n");
+ for (size_t i = 0; stacktrace[i] != _NULLPTR; ++i)
+ fprintf(new_output_fp, "%p\n", stacktrace[i]);
+ }
+ else
+ {
+ stacktrace_print_callback(new_output_fp, stacktrace);
+ }
+}
+#endif
+
+/**
+ * Checks whether a leak should be ignored. Its runtime performance
+ * depends on the callback nvwa#leak_whitelist_callback.
+ *
+ * @param ptr pointer to a new_ptr_list_t struct
+ * @return \c true if the leak should be whitelisted; \c false
+ * otherwise
+ */
+static bool is_leak_whitelisted(new_ptr_list_t* ptr)
+{
+ if (leak_whitelist_callback == _NULLPTR)
+ return false;
+
+ char const* file = ptr->line != 0 ? ptr->file : _NULLPTR;
+ int line = ptr->line;
+ void* addr = ptr->line == 0 ? ptr->addr : _NULLPTR;
+#if _DEBUG_NEW_REMEMBER_STACK_TRACE
+ void** stacktrace = ptr->stacktrace;
+#else
+ void** stacktrace = _NULLPTR;
+#endif
+
+ return leak_whitelist_callback(file, line, addr, stacktrace);
+}
+
+#if _DEBUG_NEW_TAILCHECK
/**
* Checks whether the padding bytes at the end of a memory block is
* tampered with.
*
- * @param ptr pointer to a new_ptr_list_t struct
- * @return \c true if the padding bytes are untouched; \c false
- * otherwise
+ * @param ptr pointer to a new_ptr_list_t struct
+ * @return \c true if the padding bytes are untouched; \c false
+ * otherwise
*/
static bool check_tail(new_ptr_list_t* ptr)
{
- const unsigned char* const pointer = (unsigned char*)ptr +
- ALIGNED_LIST_ITEM_SIZE + ptr->size;
- for (int i = 0; i < M_DEBUG_NEW_TAILCHECK; ++i)
- {
- if (pointer[i] != M_DEBUG_NEW_TAILCHECK_CHAR)
+ const unsigned char* const tail_ptr = (unsigned char*)ptr +
+ ALIGNED_LIST_ITEM_SIZE + ptr->size;
+ for (int i = 0; i < _DEBUG_NEW_TAILCHECK; ++i)
+ if (tail_ptr[i] != _DEBUG_NEW_TAILCHECK_CHAR)
return false;
- }
return true;
}
#endif
@@ -456,48 +573,77 @@ static bool check_tail(new_ptr_list_t* ptr)
* @param file null-terminated string of the file name
* @param line line number
* @param is_array boolean value whether this is an array operation
- * @return pointer to the user-requested memory area; \c NULL
- * if memory allocation is not successful
+ * @return pointer to the user-requested memory area; null if
+ * memory allocation is not successful
*/
static void* alloc_mem(size_t size, const char* file, int line, bool is_array)
{
assert(line >= 0);
- STATIC_ASSERT((M_DEBUG_NEW_ALIGNMENT & (M_DEBUG_NEW_ALIGNMENT - 1)) == 0,
+#if _DEBUG_NEW_TYPE == 1
+ STATIC_ASSERT(_DEBUG_NEW_ALIGNMENT >= PLATFORM_MEM_ALIGNMENT,
+ Alignment_too_small);
+#endif
+ STATIC_ASSERT((_DEBUG_NEW_ALIGNMENT & (_DEBUG_NEW_ALIGNMENT - 1)) == 0,
Alignment_must_be_power_of_two);
- STATIC_ASSERT(M_DEBUG_NEW_TAILCHECK >= 0, Invalid_tail_check_length);
- size_t s = size + ALIGNED_LIST_ITEM_SIZE + M_DEBUG_NEW_TAILCHECK;
- new_ptr_list_t* ptr = static_cast<new_ptr_list_t*>(malloc(s));
- if (ptr == nullptr)
+ STATIC_ASSERT(_DEBUG_NEW_TAILCHECK >= 0, Invalid_tail_check_length);
+ size_t s = size + ALIGNED_LIST_ITEM_SIZE + _DEBUG_NEW_TAILCHECK;
+ new_ptr_list_t* ptr = (new_ptr_list_t*)malloc(s);
+ if (ptr == _NULLPTR)
{
-#if M_DEBUG_NEW_STD_OPER_NEW
- return nullptr;
+#if _DEBUG_NEW_STD_OPER_NEW
+ return _NULLPTR;
#else
fast_mutex_autolock lock(new_output_lock);
fprintf(new_output_fp,
- "Out of memory when allocating %u bytes\n",
- size);
+ "Out of memory when allocating %lu bytes\n",
+ (unsigned long)size);
fflush(new_output_fp);
- M_DEBUG_NEW_ERROR_ACTION;
+ _DEBUG_NEW_ERROR_ACTION;
#endif
}
- void* pointer = reinterpret_cast<char*>(ptr) + ALIGNED_LIST_ITEM_SIZE;
-#if M_DEBUG_NEW_FILENAME_LEN == 0
+ void* usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE;
+#if _DEBUG_NEW_FILENAME_LEN == 0
ptr->file = file;
#else
if (line)
- {
- strncpy(ptr->file, file, M_DEBUG_NEW_FILENAME_LEN - 1)
- [M_DEBUG_NEW_FILENAME_LEN - 1] = '\0';
- }
+ strncpy(ptr->file, file, _DEBUG_NEW_FILENAME_LEN - 1)
+ [_DEBUG_NEW_FILENAME_LEN - 1] = '\0';
else
+ ptr->addr = (void*)file;
+#endif
+ ptr->line = line;
+#if _DEBUG_NEW_REMEMBER_STACK_TRACE
+ ptr->stacktrace = _NULLPTR;
+
+#if _DEBUG_NEW_REMEMBER_STACK_TRACE == 2
+ if (line == 0)
+#endif
{
- ptr->addr = reinterpret_cast<void*>(const_cast<char*>(file));
+ void* buffer [255];
+ size_t buffer_length = sizeof(buffer) / sizeof(*buffer);
+
+#if NVWA_UNIX
+ int stacktrace_length = backtrace(buffer, int(buffer_length));
+#endif
+
+#if NVWA_WINDOWS
+ USHORT stacktrace_length = CaptureStackBackTrace(
+ 0, DWORD(buffer_length), buffer, _NULLPTR);
+#endif
+
+ size_t stacktrace_size = stacktrace_length * sizeof(void*);
+ ptr->stacktrace = (void**)malloc(stacktrace_size + sizeof(void*));
+
+ if (ptr->stacktrace != _NULLPTR)
+ {
+ memcpy(ptr->stacktrace, buffer, stacktrace_size);
+ ptr->stacktrace[stacktrace_length] = _NULLPTR;
+ }
}
#endif
- ptr->line = line;
ptr->is_array = is_array;
ptr->size = size;
- ptr->magic = MAGIC;
+ ptr->magic = DEBUG_NEW_MAGIC;
{
fast_mutex_autolock lock(new_ptr_lock);
ptr->prev = new_ptr_list.prev;
@@ -505,18 +651,17 @@ static void* alloc_mem(size_t size, const char* file, int line, bool is_array)
new_ptr_list.prev->next = ptr;
new_ptr_list.prev = ptr;
}
- ptr->dumped = 0;
-#if M_DEBUG_NEW_TAILCHECK
- memset((char*)pointer + size, M_DEBUG_NEW_TAILCHECK_CHAR,
- M_DEBUG_NEW_TAILCHECK);
+#if _DEBUG_NEW_TAILCHECK
+ memset((char*)usr_ptr + size, _DEBUG_NEW_TAILCHECK_CHAR,
+ _DEBUG_NEW_TAILCHECK);
#endif
if (new_verbose_flag)
{
fast_mutex_autolock lock(new_output_lock);
fprintf(new_output_fp,
- "new%s: allocated %p (size %u, ",
+ "new%s: allocated %p (size %lu, ",
is_array ? "[]" : "",
- pointer, CAST_U32(size));
+ usr_ptr, (unsigned long)size);
if (line != 0)
print_position(ptr->file, ptr->line);
else
@@ -524,49 +669,49 @@ static void* alloc_mem(size_t size, const char* file, int line, bool is_array)
fprintf(new_output_fp, ")\n");
}
total_mem_alloc += size;
- return pointer;
+ return usr_ptr;
}
/**
* Frees memory and adjusts pointers.
*
- * @param pointer pointer to delete
+ * @param usr_ptr pointer to the previously allocated memory
* @param addr pointer to the caller
* @param is_array flag indicating whether it is invoked by a
- * <code>delete []</code> call
+ * <code>delete[]</code> call
*/
-static void free_pointer(void* pointer, void* addr, bool is_array)
+static void free_pointer(void* usr_ptr, void* addr, bool is_array)
{
- if (pointer == nullptr)
+ if (usr_ptr == _NULLPTR)
return;
- new_ptr_list_t* ptr = reinterpret_cast<new_ptr_list_t*>(
- static_cast<char*>(pointer) - ALIGNED_LIST_ITEM_SIZE);
- if (ptr->magic != MAGIC)
+ new_ptr_list_t* ptr =
+ (new_ptr_list_t*)((char*)usr_ptr - ALIGNED_LIST_ITEM_SIZE);
+ if (ptr->magic != DEBUG_NEW_MAGIC)
{
{
fast_mutex_autolock lock(new_output_lock);
fprintf(new_output_fp, "delete%s: invalid pointer %p (",
- is_array ? "[]" : "", pointer);
+ is_array ? "[]" : "", usr_ptr);
print_position(addr, 0);
fprintf(new_output_fp, ")\n");
}
check_mem_corruption();
fflush(new_output_fp);
- M_DEBUG_NEW_ERROR_ACTION;
+ _DEBUG_NEW_ERROR_ACTION;
}
if (is_array != ptr->is_array)
{
const char* msg;
if (is_array)
- msg = "delete [] after new";
+ msg = "delete[] after new";
else
- msg = "delete after new []";
+ msg = "delete after new[]";
fast_mutex_autolock lock(new_output_lock);
fprintf(new_output_fp,
- "%s: pointer %p (size %u)\n\tat ",
+ "%s: pointer %p (size %lu)\n\tat ",
msg,
- reinterpret_cast<char*>(ptr) + ALIGNED_LIST_ITEM_SIZE,
- CAST_U32(ptr->size));
+ (char*)ptr + ALIGNED_LIST_ITEM_SIZE,
+ (unsigned long)ptr->size);
print_position(addr, 0);
fprintf(new_output_fp, "\n\toriginally allocated at ");
if (ptr->line != 0)
@@ -575,14 +720,14 @@ static void free_pointer(void* pointer, void* addr, bool is_array)
print_position(ptr->addr, ptr->line);
fprintf(new_output_fp, "\n");
fflush(new_output_fp);
- M_DEBUG_NEW_ERROR_ACTION;
+ _DEBUG_NEW_ERROR_ACTION;
}
-#if M_DEBUG_NEW_TAILCHECK
+#if _DEBUG_NEW_TAILCHECK
if (!check_tail(ptr))
{
check_mem_corruption();
fflush(new_output_fp);
- M_DEBUG_NEW_ERROR_ACTION;
+ _DEBUG_NEW_ERROR_ACTION;
}
#endif
{
@@ -596,12 +741,14 @@ static void free_pointer(void* pointer, void* addr, bool is_array)
{
fast_mutex_autolock lock(new_output_lock);
fprintf(new_output_fp,
- "delete%s: freed %p (size %u, %u bytes still allocated)\n",
- is_array ? "[]" : "",
- reinterpret_cast<char*>(ptr) + ALIGNED_LIST_ITEM_SIZE,
- CAST_U32(ptr->size),
- CAST_U32(total_mem_alloc));
+ "delete%s: freed %p (size %lu, %lu bytes still allocated)\n",
+ is_array ? "[]" : "",
+ (char*)ptr + ALIGNED_LIST_ITEM_SIZE,
+ (unsigned long)ptr->size, (unsigned long)total_mem_alloc);
}
+#if _DEBUG_NEW_REMEMBER_STACK_TRACE
+ free(ptr->stacktrace);
+#endif
free(ptr);
return;
}
@@ -614,60 +761,69 @@ static void free_pointer(void* pointer, void* addr, bool is_array)
int check_leaks()
{
int leak_cnt = 0;
- int dumped_cnt = 0;
- int new_cnt = 0;
- unsigned long new_size = 0;
+ int whitelisted_leak_cnt = 0;
fast_mutex_autolock lock_ptr(new_ptr_lock);
fast_mutex_autolock lock_output(new_output_lock);
new_ptr_list_t* ptr = new_ptr_list.next;
- fprintf(new_output_fp, "---LEAKS LIST---\n");
while (ptr != &new_ptr_list)
{
- const char* const pointer = reinterpret_cast<const char *>(ptr)
- + ALIGNED_LIST_ITEM_SIZE;
- if (ptr->magic != MAGIC)
+ const char* const usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE;
+ if (ptr->magic != DEBUG_NEW_MAGIC)
{
fprintf(new_output_fp,
"warning: heap data corrupt near %p\n",
- pointer);
+ usr_ptr);
}
-#if M_DEBUG_NEW_TAILCHECK
+#if _DEBUG_NEW_TAILCHECK
if (!check_tail(ptr))
{
fprintf(new_output_fp,
"warning: overwritten past end of object at %p\n",
- pointer);
+ usr_ptr);
}
#endif
-#ifndef DUMP_MEM_ADDRESSES
- if (ptr->line != 0)
-#endif
+
+ if (is_leak_whitelisted(ptr))
+ {
+ ++whitelisted_leak_cnt;
+ }
+ else
{
fprintf(new_output_fp,
- "Leaked object at %p (size %u, dump %u, ",
- pointer, CAST_U32(ptr->size), ptr->dumped);
+ "Leaked object at %p (size %lu, ",
+ usr_ptr,
+ (unsigned long)ptr->size);
+
if (ptr->line != 0)
print_position(ptr->file, ptr->line);
else
print_position(ptr->addr, ptr->line);
+
fprintf(new_output_fp, ")\n");
- ++ new_cnt;
- new_size += static_cast<unsigned long>(ptr->size);
+
+#if _DEBUG_NEW_REMEMBER_STACK_TRACE
+ if (ptr->stacktrace != _NULLPTR)
+ print_stacktrace(ptr->stacktrace);
+#endif
}
- if (ptr->dumped)
- ++ dumped_cnt;
- ++ ptr->dumped;
ptr = ptr->next;
- ++ leak_cnt;
+ ++leak_cnt;
}
if (new_verbose_flag || leak_cnt)
{
- fprintf(new_output_fp, "*** %d leaks found, new %d "
- "(size %lu), dumped count %d\n", leak_cnt, new_cnt,
- new_size, dumped_cnt);
+ if (whitelisted_leak_cnt > 0)
+ {
+ fprintf(new_output_fp, "*** %d leaks found (%d whitelisted)\n",
+ leak_cnt, whitelisted_leak_cnt);
+ }
+ else
+ {
+ fprintf(new_output_fp, "*** %d leaks found\n", leak_cnt);
+ }
}
+
return leak_cnt;
}
@@ -684,35 +840,32 @@ int check_mem_corruption()
fast_mutex_autolock lock_output(new_output_lock);
fprintf(new_output_fp, "*** Checking for memory corruption: START\n");
for (new_ptr_list_t* ptr = new_ptr_list.next;
- ptr != &new_ptr_list;
- ptr = ptr->next)
+ ptr != &new_ptr_list;
+ ptr = ptr->next)
{
- const char* const pointer = reinterpret_cast<char*>(ptr)
- + ALIGNED_LIST_ITEM_SIZE;
- if (ptr->magic == MAGIC
-#if M_DEBUG_NEW_TAILCHECK
- && check_tail(ptr)
+ const char* const usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE;
+ if (ptr->magic == DEBUG_NEW_MAGIC
+#if _DEBUG_NEW_TAILCHECK
+ && check_tail(ptr)
#endif
- )
- {
+ )
continue;
- }
-#if M_DEBUG_NEW_TAILCHECK
- if (ptr->magic != MAGIC)
+#if _DEBUG_NEW_TAILCHECK
+ if (ptr->magic != DEBUG_NEW_MAGIC)
{
#endif
fprintf(new_output_fp,
- "Heap data corrupt near %p (size %u, ",
- pointer,
- CAST_U32(ptr->size));
-#if M_DEBUG_NEW_TAILCHECK
+ "Heap data corrupt near %p (size %lu, ",
+ usr_ptr,
+ (unsigned long)ptr->size);
+#if _DEBUG_NEW_TAILCHECK
}
else
{
fprintf(new_output_fp,
- "Overwritten past end of object at %p (size %u, ",
- pointer,
- ptr->size);
+ "Overwritten past end of object at %p (size %lu, ",
+ usr_ptr,
+ (unsigned long)ptr->size);
}
#endif
if (ptr->line != 0)
@@ -720,20 +873,50 @@ int check_mem_corruption()
else
print_position(ptr->addr, ptr->line);
fprintf(new_output_fp, ")\n");
- ++ corrupt_cnt;
+
+#if _DEBUG_NEW_REMEMBER_STACK_TRACE
+ if (ptr->stacktrace != _NULLPTR)
+ print_stacktrace(ptr->stacktrace);
+#endif
+
+ ++corrupt_cnt;
}
fprintf(new_output_fp, "*** Checking for memory corruption: %d FOUND\n",
corrupt_cnt);
return corrupt_cnt;
}
-void __debug_new_recorder::_M_process(void* pointer)
+/**
+ * Processes the allocated memory and inserts file/line informatin.
+ * It will only be done when it can ensure the memory is allocated by
+ * one of our operator new variants.
+ *
+ * @param usr_ptr pointer returned by a new-expression
+ */
+void debug_new_recorder::_M_process(void* usr_ptr)
{
- if (pointer == nullptr)
+ if (usr_ptr == _NULLPTR)
return;
- new_ptr_list_t* ptr = reinterpret_cast<new_ptr_list_t*>(
- static_cast<char*>(pointer) - ALIGNED_LIST_ITEM_SIZE);
- if (ptr->magic != MAGIC || ptr->line != 0)
+
+ // In an expression `new NonPODType[size]', the pointer returned is
+ // not the pointer returned by operator new[], but offset by size_t
+ // to leave room for the size. It needs to be compensated here.
+ size_t offset = (char*)usr_ptr - (char*)_NULLPTR;
+ if (offset % PLATFORM_MEM_ALIGNMENT != 0) {
+ offset -= sizeof(size_t);
+ if (offset % PLATFORM_MEM_ALIGNMENT != 0) {
+ fast_mutex_autolock lock(new_output_lock);
+ fprintf(new_output_fp,
+ "warning: memory unaligned; skipping processing (%s:%d)\n",
+ _M_file, _M_line);
+ return;
+ }
+ usr_ptr = (char*)usr_ptr - sizeof(size_t);
+ }
+
+ new_ptr_list_t* ptr =
+ (new_ptr_list_t*)((char*)usr_ptr - ALIGNED_LIST_ITEM_SIZE);
+ if (ptr->magic != DEBUG_NEW_MAGIC || ptr->line != 0)
{
fast_mutex_autolock lock(new_output_lock);
fprintf(new_output_fp,
@@ -741,19 +924,78 @@ void __debug_new_recorder::_M_process(void* pointer)
_M_file, _M_line);
return;
}
-#if M_DEBUG_NEW_FILENAME_LEN == 0
+ if (new_verbose_flag) {
+ fast_mutex_autolock lock(new_output_lock);
+ fprintf(new_output_fp,
+ "info: pointer %p allocated from %s:%d\n",
+ usr_ptr, _M_file, _M_line);
+ }
+#if _DEBUG_NEW_FILENAME_LEN == 0
ptr->file = _M_file;
#else
- strncpy(ptr->file, _M_file, M_DEBUG_NEW_FILENAME_LEN - 1)
- [M_DEBUG_NEW_FILENAME_LEN - 1] = '\0';
+ strncpy(ptr->file, _M_file, _DEBUG_NEW_FILENAME_LEN - 1)
+ [_DEBUG_NEW_FILENAME_LEN - 1] = '\0';
#endif
ptr->line = _M_line;
+#if _DEBUG_NEW_REMEMBER_STACK_TRACE == 2
+ free(ptr->stacktrace);
+ ptr->stacktrace = _NULLPTR;
+#endif
+}
+
+/**
+ * Count of source files that use debug_new.
+ */
+int debug_new_counter::_S_count = 0;
+
+/**
+ * Constructor to increment the count.
+ */
+debug_new_counter::debug_new_counter()
+{
+ ++_S_count;
+}
+
+/**
+ * Destructor to decrement the count. When the count is zero,
+ * nvwa#check_leaks will be called.
+ */
+debug_new_counter::~debug_new_counter()
+{
+ if (--_S_count == 0 && new_autocheck_flag)
+ if (check_leaks())
+ {
+ new_verbose_flag = true;
+#if defined(__GNUC__) && __GNUC__ == 3
+ if (!getenv("GLIBCPP_FORCE_NEW") && !getenv("GLIBCXX_FORCE_NEW"))
+ fprintf(new_output_fp,
+"*** WARNING: GCC 3 is detected, please make sure the environment\n"
+" variable GLIBCPP_FORCE_NEW (GCC 3.2 and 3.3) or GLIBCXX_FORCE_NEW\n"
+" (GCC 3.4) is defined. Check the README file for details.\n");
+#endif
+ }
}
-void* operator new (size_t size, const char* file, int line)
+NVWA_NAMESPACE_END
+
+#if NVWA_USE_NAMESPACE
+using namespace nvwa;
+#endif // NVWA_USE_NAMESPACE
+
+/**
+ * Allocates memory with file/line information.
+ *
+ * @param size size of the required memory block
+ * @param file null-terminated string of the file name
+ * @param line line number
+ * @return pointer to the memory allocated; or null if memory is
+ * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0)
+ * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1)
+ */
+void* operator new(size_t size, const char* file, int line)
{
void* ptr = alloc_mem(size, file, line, false);
-#if M_DEBUG_NEW_STD_OPER_NEW
+#if _DEBUG_NEW_STD_OPER_NEW
if (ptr)
return ptr;
else
@@ -763,10 +1005,20 @@ void* operator new (size_t size, const char* file, int line)
#endif
}
-void* operator new [](size_t size, const char* file, int line)
+/**
+ * Allocates array memory with file/line information.
+ *
+ * @param size size of the required memory block
+ * @param file null-terminated string of the file name
+ * @param line line number
+ * @return pointer to the memory allocated; or null if memory is
+ * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0)
+ * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1)
+ */
+void* operator new[](size_t size, const char* file, int line)
{
void* ptr = alloc_mem(size, file, line, true);
-#if M_DEBUG_NEW_STD_OPER_NEW
+#if _DEBUG_NEW_STD_OPER_NEW
if (ptr)
return ptr;
else
@@ -776,113 +1028,159 @@ void* operator new [](size_t size, const char* file, int line)
#endif
}
-void* operator new (size_t size) // throw(std::bad_alloc)
+/**
+ * Allocates memory without file/line information.
+ *
+ * @param size size of the required memory block
+ * @return pointer to the memory allocated; or null if memory is
+ * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0)
+ * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1)
+ */
+void* operator new(size_t size) throw(std::bad_alloc)
+{
+ return operator new(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
+}
+
+/**
+ * Allocates array memory without file/line information.
+ *
+ * @param size size of the required memory block
+ * @return pointer to the memory allocated; or null if memory is
+ * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0)
+ * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1)
+ */
+void* operator new[](size_t size) throw(std::bad_alloc)
{
- return operator new (size, static_cast<char*>(
- M_DEBUG_NEW_CALLER_ADDRESS), 0);
+ return operator new[](size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
}
-void* operator new [](size_t size) // throw(std::bad_alloc)
+/**
+ * Allocates memory with no-throw guarantee.
+ *
+ * @param size size of the required memory block
+ * @return pointer to the memory allocated; or null if memory is
+ * insufficient
+ */
+void* operator new(size_t size, const std::nothrow_t&) _NOEXCEPT
{
- return operator new [](size, static_cast<char*>(
- M_DEBUG_NEW_CALLER_ADDRESS), 0);
+ return alloc_mem(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0, false);
}
-#if !defined(__BORLANDC__) || __BORLANDC__ > 0x551
-void* operator new (size_t size, const std::nothrow_t&) throw()
+/**
+ * Allocates array memory with no-throw guarantee.
+ *
+ * @param size size of the required memory block
+ * @return pointer to the memory allocated; or null if memory is
+ * insufficient
+ */
+void* operator new[](size_t size, const std::nothrow_t&) _NOEXCEPT
{
- return alloc_mem(size, static_cast<char*>(
- M_DEBUG_NEW_CALLER_ADDRESS), 0, false);
+ return alloc_mem(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0, true);
}
-void* operator new [](size_t size, const std::nothrow_t&) throw()
+/**
+ * Deallocates memory.
+ *
+ * @param ptr pointer to the previously allocated memory
+ */
+void operator delete(void* ptr) _NOEXCEPT
{
- return alloc_mem(size, static_cast<char*>(
- M_DEBUG_NEW_CALLER_ADDRESS), 0, true);
+ free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, false);
}
-#endif
-void operator delete (void* pointer) throw()
+/**
+ * Deallocates array memory.
+ *
+ * @param ptr pointer to the previously allocated memory
+ */
+void operator delete[](void* ptr) _NOEXCEPT
+{
+ free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, true);
+}
+
+#if __cplusplus >= 201402L
+// GCC under C++14 wants these definitions
+
+void operator delete(void* ptr, size_t) _NOEXCEPT
{
- free_pointer(pointer, M_DEBUG_NEW_CALLER_ADDRESS, false);
+ free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, false);
}
-void operator delete [](void* pointer) throw()
+void operator delete[](void* ptr, size_t) _NOEXCEPT
{
- free_pointer(pointer, M_DEBUG_NEW_CALLER_ADDRESS, true);
+ free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, true);
}
+#endif
-#if HAVE_PLACEMENT_DELETE
-void operator delete (void* pointer, const char* file, int line) throw()
+/**
+ * Placement deallocation function. For details, please check Section
+ * 5.3.4 of the C++ 1998 or 2011 Standard.
+ *
+ * @param ptr pointer to the previously allocated memory
+ * @param file null-terminated string of the file name
+ * @param line line number
+ *
+ * @see http://www.csci.csusb.edu/dick/c++std/cd2/expr.html#expr.new
+ * @see http://wyw.dcweb.cn/leakage.htm
+ */
+void operator delete(void* ptr, const char* file, int line) _NOEXCEPT
{
if (new_verbose_flag)
{
fast_mutex_autolock lock(new_output_lock);
fprintf(new_output_fp,
"info: exception thrown on initializing object at %p (",
- pointer);
+ ptr);
print_position(file, line);
fprintf(new_output_fp, ")\n");
}
- operator delete (pointer);
+ operator delete(ptr);
}
-void operator delete [](void* pointer, const char* file, int line) throw()
+/**
+ * Placement deallocation function. For details, please check Section
+ * 5.3.4 of the C++ 1998 or 2011 Standard.
+ *
+ * @param ptr pointer to the previously allocated memory
+ * @param file null-terminated string of the file name
+ * @param line line number
+ */
+void operator delete[](void* ptr, const char* file, int line) _NOEXCEPT
{
if (new_verbose_flag)
{
fast_mutex_autolock lock(new_output_lock);
fprintf(new_output_fp,
"info: exception thrown on initializing objects at %p (",
- pointer);
+ ptr);
print_position(file, line);
fprintf(new_output_fp, ")\n");
}
- operator delete [](pointer);
-}
-
-void operator delete (void* pointer, const std::nothrow_t&) throw()
-{
- operator delete (pointer, static_cast<char*>(
- M_DEBUG_NEW_CALLER_ADDRESS), 0);
+ operator delete[](ptr);
}
-void operator delete [](void* pointer, const std::nothrow_t&) throw()
-{
- operator delete [](pointer, static_cast<char*>(
- M_DEBUG_NEW_CALLER_ADDRESS), 0);
-}
-#endif // HAVE_PLACEMENT_DELETE
-
-int __debug_new_counter::_S_count = 0;
-
/**
- * Constructor to increment the count.
+ * Placement deallocation function. For details, please check Section
+ * 5.3.4 of the C++ 1998 or 2011 Standard.
+ *
+ * @param ptr pointer to the previously allocated memory
*/
-__debug_new_counter::__debug_new_counter()
+void operator delete(void* ptr, const std::nothrow_t&) _NOEXCEPT
{
- ++_S_count;
+ operator delete(ptr, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
}
/**
- * Destructor to decrement the count. When the count is zero,
- * #check_leaks will be called.
+ * Placement deallocation function. For details, please check Section
+ * 5.3.4 of the C++ 1998 or 2011 Standard.
+ *
+ * @param ptr pointer to the previously allocated memory
*/
-__debug_new_counter::~__debug_new_counter()
+void operator delete[](void* ptr, const std::nothrow_t&) _NOEXCEPT
{
- if (--_S_count == 0 && new_autocheck_flag)
- if (check_leaks())
- {
- new_verbose_flag = true;
-#if defined(__GNUC__) && __GNUC__ >= 3
- if (!getenv("GLIBCPP_FORCE_NEW") && !getenv("GLIBCXX_FORCE_NEW"))
- fprintf(new_output_fp,
-"*** WARNING: GCC 3 or later is detected, please make sure the\n"
-" environment variable GLIBCPP_FORCE_NEW (GCC 3.2 and 3.3) or\n"
-" GLIBCXX_FORCE_NEW (GCC 3.4 and later) is defined. Check the\n"
-" README file for details.\n");
-#endif
- }
+ operator delete[](ptr, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
}
-#endif
+// This is to make Doxygen happy
+#undef _DEBUG_NEW_REMEMBER_STACK_TRACE
+#define _DEBUG_NEW_REMEMBER_STACK_TRACE 0
diff --git a/src/debug/debug_new.h b/src/debug/debug_new.h
index e4e78c003..926de99de 100644
--- a/src/debug/debug_new.h
+++ b/src/debug/debug_new.h
@@ -2,7 +2,7 @@
// vim:tabstop=4:shiftwidth=4:expandtab:
/*
- * Copyright (C) 2004-2008 Wu Yongwei <adah at users dot sourceforge dot net>
+ * Copyright (C) 2004-2015 Wu Yongwei <adah at users dot sourceforge dot net>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
@@ -24,50 +24,40 @@
* This file is part of Stones of Nvwa:
* http://sourceforge.net/projects/nvwa
*
- * original version changed for ManaPlus
- *
- * Copyright (C) 2011-2017 The ManaPlus Developers
*/
/**
- * @file debug_new.h
+ * @file debug_new.h
*
* Header file for checking leaks caused by unmatched new/delete.
*
- * @version 4.4, 2007/12/31
- * @author Wu Yongwei
- *
+ * @date 2015-10-25
*/
-#ifndef M_DEBUG_NEW_H
-#define M_DEBUG_NEW_H
+#ifndef NVWA_DEBUG_NEW_H
+#define NVWA_DEBUG_NEW_H
-#include <new>
-#include <cstdio>
+#include <new> // size_t/std::bad_alloc
+#include <stdio.h> // FILE
+#include "debug/_nvwa.h" // NVWA_NAMESPACE_*
+#include "debug/c++11.h" // _NOEXCEPT
-/**
- * @def HAVE_PLACEMENT_DELETE
- *
- * Macro to indicate whether placement delete operators are supported on
- * a certain compiler. Some compilers, like Borland C++ Compiler 5.5.1
- * and Digital Mars Compiler 8.42, do not support them, and the user
- * must define this macro to \c 0 to make the program compile. Also
- * note that in that case memory leakage will occur if an exception is
- * thrown in the initialization (constructor) of a dynamically created
- * object.
- */
-#ifndef HAVE_PLACEMENT_DELETE
-#define HAVE_PLACEMENT_DELETE 1
-#endif
+/* Special allocation/deallocation functions in the global scope */
+void* operator new(size_t size, const char* file, int line);
+void* operator new[](size_t size, const char* file, int line);
+void operator delete(void* ptr, const char* file, int line) _NOEXCEPT;
+void operator delete[](void* ptr, const char* file, int line) _NOEXCEPT;
+
+NVWA_NAMESPACE_BEGIN
/**
- * @def M_DEBUG_NEW_REDEFINE_NEW
+ * @def _DEBUG_NEW_REDEFINE_NEW
*
* Macro to indicate whether redefinition of \c new is wanted. If one
- * wants to define one's own <code>operator new</code>, to call
- * <code>operator new</code> directly, or to call placement \c new, it
- * should be defined to \c 0 to alter the default behaviour. Unless, of
- * course, one is willing to take the trouble to write something like:
+ * wants to define one's own <code>operator new</code>, or to call
+ * <code>operator new</code> directly, it should be defined to \c 0 to
+ * alter the default behaviour. Unless, of course, one is willing to
+ * take the trouble to write something like:
* @code
* # ifdef new
* # define _NEW_REDEFINED
@@ -84,47 +74,79 @@
* # endif
* @endcode
*/
-#ifndef M_DEBUG_NEW_REDEFINE_NEW
-#define M_DEBUG_NEW_REDEFINE_NEW 1
+#ifndef _DEBUG_NEW_REDEFINE_NEW
+#define _DEBUG_NEW_REDEFINE_NEW 1
+#endif
+
+/**
+ * @def _DEBUG_NEW_TYPE
+ *
+ * Macro to indicate which variant of #DEBUG_NEW is wanted. The
+ * default value \c 1 allows the use of placement new (like
+ * <code>%new(std::nothrow)</code>), but the verbose output (when
+ * nvwa#new_verbose_flag is \c true) looks worse than some older
+ * versions (no file/line information for allocations). Define it
+ * to \c 2 to revert to the old behaviour that records file and line
+ * information directly on the call to <code>operator new</code>.
+ */
+#ifndef _DEBUG_NEW_TYPE
+#define _DEBUG_NEW_TYPE 1
#endif
+/**
+ * Callback type for stack trace printing.
+ *
+ * @param fp pointer to the output stream
+ * @param stacktrace pointer to the stack trace array (null-terminated)
+ */
+typedef void (*stacktrace_print_callback_t)(FILE* fp, void** stacktrace);
+
+/**
+ * Callback type for the leak whitelist function. \a file, \a address,
+ * and \a backtrace might be null depending on library configuration,
+ * platform, and amount of runtime information available. \a line can
+ * be 0 when line number info is not available at runtime.
+ *
+ * @param file null-terminated string of the file name
+ * @param line line number
+ * @param addr address of code where leakage happens
+ * @param stacktrace pointer to the stack trace array (null-terminated)
+ * @return \c true if the leak should be whitelisted;
+ * \c false otherwise
+ */
+typedef bool (*leak_whitelist_callback_t)(char const* file, int line,
+ void* addr, void** stacktrace);
+
/* Prototypes */
int check_leaks();
int check_mem_corruption();
-void* operator new(size_t size, const char* file, int line);
-void* operator new[](size_t size, const char* file, int line);
-#if HAVE_PLACEMENT_DELETE
-void operator delete(void* pointer, const char* file, int line) throw();
-void operator delete[](void* pointer, const char* file, int line) throw();
-#endif
-#if defined(_MSC_VER) && _MSC_VER < 1300
-// MSVC 6 requires the following declarations; or the non-placement
-// new[]/delete[] will not compile.
-void* operator new[](size_t) throw(std::bad_alloc);
-void operator delete[](void*) throw();
-#endif
/* Control variables */
-extern bool new_autocheck_flag; // default to true: call check_leaks() on exit
-extern bool new_verbose_flag; // default to false: no verbose information
-extern FILE* new_output_fp; // default to stderr: output to console
-extern const char* new_progname; // default to NULL; should be
- // assigned argv[0]
+extern bool new_autocheck_flag; // default to true: call check_leaks() on exit
+extern bool new_verbose_flag; // default to false: no verbose information
+extern FILE* new_output_fp; // default to stderr: output to console
+extern const char* new_progname;// default to null; should be assigned argv[0]
+extern stacktrace_print_callback_t stacktrace_print_callback;// default to null
+extern leak_whitelist_callback_t leak_whitelist_callback; // default to null
/**
* @def DEBUG_NEW
*
* Macro to catch file/line information on allocation. If
- * #M_DEBUG_NEW_REDEFINE_NEW is \c 0, one can use this macro directly;
+ * #_DEBUG_NEW_REDEFINE_NEW is \c 0, one can use this macro directly;
* otherwise \c new will be defined to it, and one must use \c new
* instead.
*/
-#define DEBUG_NEW __debug_new_recorder(__FILE__, __LINE__) ->* new
+# if _DEBUG_NEW_TYPE == 1
+# define DEBUG_NEW NVWA::debug_new_recorder(__FILE__, __LINE__) ->* new
+# else
+# define DEBUG_NEW new(__FILE__, __LINE__)
+# endif
-# if M_DEBUG_NEW_REDEFINE_NEW
+# if _DEBUG_NEW_REDEFINE_NEW
# define new DEBUG_NEW
# endif
-# ifdef M_DEBUG_NEW_EMULATE_MALLOC
+# ifdef _DEBUG_NEW_EMULATE_MALLOC
# include <stdlib.h>
# ifdef new
# define malloc(s) ((void*)(new char[s]))
@@ -132,7 +154,6 @@ extern const char* new_progname; // default to NULL; should be
# define malloc(s) ((void*)(DEBUG_NEW char[s]))
# endif
# define free(p) delete[] (char*)(p)
-# define default_free free
# endif
/**
@@ -140,29 +161,29 @@ extern const char* new_progname; // default to NULL; should be
*
* The idea comes from <a href="http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/7089382e3bc1c489/85f9107a1dc79ee9?#85f9107a1dc79ee9">Greg Herlihy's post</a> in comp.lang.c++.moderated.
*/
-class __debug_new_recorder
+class debug_new_recorder
{
- const char* _M_file;
- const int _M_line;
- void _M_process(void* pointer);
- public:
- /**
- * Constructor to remember the call context. The information will
- * be used in __debug_new_recorder::operator->*.
- */
- __debug_new_recorder(const char* file, int line)
- : _M_file(file), _M_line(line) {}
- /**
- * Operator to write the context information to memory.
- * <code>operator->*</code> is chosen because it has the right
- * precedence, it is rarely used, and it looks good: so people can
- * tell the special usage more quickly.
- */
- template <class _Tp> _Tp* operator->*(_Tp* pointer)
- { _M_process(pointer); return pointer; }
- private:
- __debug_new_recorder(const __debug_new_recorder&);
- __debug_new_recorder& operator=(const __debug_new_recorder&);
+ const char* _M_file;
+ const int _M_line;
+ void _M_process(void* ptr);
+public:
+ /**
+ * Constructor to remember the call context. The information will
+ * be used in debug_new_recorder::operator->*.
+ */
+ debug_new_recorder(const char* file, int line)
+ : _M_file(file), _M_line(line) {}
+ /**
+ * Operator to write the context information to memory.
+ * <code>operator->*</code> is chosen because it has the right
+ * precedence, it is rarely used, and it looks good: so people can
+ * tell the special usage more quickly.
+ */
+ template <class _Tp> _Tp* operator->*(_Tp* ptr)
+ { _M_process(ptr); return ptr; }
+private:
+ debug_new_recorder(const debug_new_recorder&);
+ debug_new_recorder& operator=(const debug_new_recorder&);
};
/**
@@ -171,14 +192,16 @@ class __debug_new_recorder
* This technique is learnt from <em>The C++ Programming Language</em> by
* Bjarne Stroustup.
*/
-class __debug_new_counter
+class debug_new_counter
{
- static int _S_count;
- public:
- __debug_new_counter();
- ~__debug_new_counter();
+ static int _S_count;
+public:
+ debug_new_counter();
+ ~debug_new_counter();
};
/** Counting object for each file including debug_new.h. */
-static __debug_new_counter __debug_new_count;
+static debug_new_counter __debug_new_count;
+
+NVWA_NAMESPACE_END
-#endif // M_DEBUG_NEW_H
+#endif // NVWA_DEBUG_NEW_H
diff --git a/src/debug/fast_mutex.h b/src/debug/fast_mutex.h
index f648fdc2a..bd9fc37d3 100644
--- a/src/debug/fast_mutex.h
+++ b/src/debug/fast_mutex.h
@@ -2,7 +2,7 @@
// vim:tabstop=4:shiftwidth=4:expandtab:
/*
- * Copyright (C) 2004-2008 Wu Yongwei <adah at users dot sourceforge dot net>
+ * Copyright (C) 2004-2015 Wu Yongwei <adah at users dot sourceforge dot net>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
@@ -24,27 +24,40 @@
* This file is part of Stones of Nvwa:
* http://sourceforge.net/projects/nvwa
*
- * original version changed for ManaPlus
- *
- * Copyright (C) 2011-2017 The ManaPlus Developers
*/
/**
- * @file fast_mutex.h
- *
- * A fast mutex implementation for POSIX and Win32.
+ * @file fast_mutex.h
*
- * @version 1.18, 2005/05/06
- * @author Wu Yongwei
+ * A fast mutex implementation for POSIX, Win32, and modern C++.
*
+ * @date 2015-05-19
*/
-#ifndef M_FAST_MUTEX_H
-#define M_FAST_MUTEX_H
+#ifndef NVWA_FAST_MUTEX_H
+#define NVWA_FAST_MUTEX_H
-#include "localconsts.h"
+#include "debug/_nvwa.h" // NVWA_NAMESPACE_*
+#include "debug/c++11.h" // HAVE_CXX11_MUTEX
# if !defined(_NOTHREADS)
+# if !defined(NVWA_USE_CXX11_MUTEX) && HAVE_CXX11_MUTEX != 0 && \
+ !defined(_WIN32THREADS) && !defined(NVWA_WIN32THREADS) && \
+ !defined(NVWA_PTHREADS) && !defined(NVWA_NOTHREADS) && \
+ defined(_WIN32) && defined(_MT) && \
+ (!defined(_MSC_VER) || defined(_DLL))
+// Prefer using std::mutex on Windows to avoid the namespace
+// pollution caused by <windows.h>. However, MSVC has a re-entry
+// issue with its std::mutex implementation, and std::mutex should
+// not be used unless /MD or /MDd is used. For more information,
+// check out:
+//
+// https://connect.microsoft.com/VisualStudio/feedback/details/776596/std-mutex-not-a-constexpr-with-mtd-compiler-flag
+// http://stackoverflow.com/questions/14319344/stdmutex-lock-hangs-when-overriding-the-new-operator
+//
+# define NVWA_USE_CXX11_MUTEX 1
+# endif
+
# if !defined(_WIN32THREADS) && \
(defined(_WIN32) && defined(_MT))
// Automatically use _WIN32THREADS when specifying -MT/-MD in MSVC,
@@ -52,24 +65,54 @@
# define _WIN32THREADS
# elif !defined(_PTHREADS) && \
defined(_REENTRANT)
-// Automatically use _PTHREADS when specifying -pthread in GCC.
-// N.B. I do not detect on _PTHREAD_H since libstdc++-v3 under
-// Linux will silently include <pthread.h> anyway.
+// Automatically use _PTHREADS when specifying -pthread in GCC or Clang.
# define _PTHREADS
# endif
# endif
-# if !defined(_PTHREADS) && !defined(_WIN32THREADS) && !defined(_NOTHREADS)
+# ifndef NVWA_USE_CXX11_MUTEX
+# if HAVE_CXX11_MUTEX != 0 && \
+ !defined(_NOTHREADS) && !defined(NVWA_NOTHREADS) && \
+ !defined(_PTHREADS) && !defined(NVWA_PTHREADS) && \
+ !defined(_WIN32THREADS) && !defined(NVWA_WIN32THREADS)
+# define NVWA_USE_CXX11_MUTEX 1
+# else
+# define NVWA_USE_CXX11_MUTEX 0
+# endif
+# endif
+
+# if !defined(_PTHREADS) && !defined(_WIN32THREADS) && \
+ !defined(_NOTHREADS) && NVWA_USE_CXX11_MUTEX == 0
# define _NOTHREADS
# endif
# if defined(_NOTHREADS)
-# if defined(_PTHREADS) || defined(_WIN32THREADS)
+# if defined(_PTHREADS) || defined(_WIN32THREADS) || \
+ NVWA_USE_CXX11_MUTEX != 0
# undef _NOTHREADS
# error "Cannot define multi-threaded mode with -D_NOTHREADS"
-# if defined(__MINGW32__) && defined(_WIN32THREADS) && !defined(_MT)
-# error "Be sure to specify -mthreads with -D_WIN32THREADS"
-# endif
+# endif
+# endif
+
+# if defined(__MINGW32__) && defined(_WIN32THREADS) && !defined(_MT)
+# error "Be sure to specify -mthreads with -D_WIN32THREADS"
+# endif
+
+// With all the heuristics above, things may still go wrong, maybe even
+// due to a specific inclusion order. So they may be overridden by
+// manually defining the NVWA_* macros below.
+# if NVWA_USE_CXX11_MUTEX == 0 && \
+ !defined(NVWA_WIN32THREADS) && \
+ !defined(NVWA_PTHREADS) && \
+ !defined(NVWA_NOTHREADS)
+// _WIN32THREADS takes precedence, as some C++ libraries have _PTHREADS
+// defined even on Win32 platforms.
+# if defined(_WIN32THREADS)
+# define NVWA_WIN32THREADS
+# elif defined(_PTHREADS)
+# define NVWA_PTHREADS
+# else
+# define NVWA_NOTHREADS
# endif
# endif
@@ -85,13 +128,8 @@
# define _FAST_MUTEX_CHECK_INITIALIZATION 1
# endif
-# if defined(_PTHREADS) && defined(_WIN32THREADS)
-// Some C++ libraries have _PTHREADS defined even on Win32 platforms.
-// Thus this hack.
-# undef _PTHREADS
-# endif
-
# ifdef _DEBUG
+# include <stdio.h>
# include <stdlib.h>
/** Macro for fast_mutex assertions. Real version (for debug mode). */
# define _FAST_MUTEX_ASSERT(_Expr, _Msg) \
@@ -105,8 +143,76 @@
((void)0)
# endif
-# ifdef _PTHREADS
+# if NVWA_USE_CXX11_MUTEX != 0
+# include <mutex>
+NVWA_NAMESPACE_BEGIN
+/**
+ * Macro alias to `volatile' semantics. Here it is truly volatile since
+ * it is in a multi-threaded (C++11) environment.
+ */
+# define __VOLATILE volatile
+ /**
+ * Class for non-reentrant fast mutexes. This is the implementation
+ * using the C++11 mutex.
+ */
+ class fast_mutex
+ {
+ std::mutex _M_mtx_impl;
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ bool _M_initialized;
+# endif
+# ifdef _DEBUG
+ bool _M_locked;
+# endif
+ public:
+ fast_mutex()
+# ifdef _DEBUG
+ : _M_locked(false)
+# endif
+ {
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ _M_initialized = true;
+# endif
+ }
+ ~fast_mutex()
+ {
+ _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked");
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ _M_initialized = false;
+# endif
+ }
+ void lock()
+ {
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ if (!_M_initialized)
+ return;
+# endif
+ _M_mtx_impl.lock();
+# ifdef _DEBUG
+ _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked");
+ _M_locked = true;
+# endif
+ }
+ void unlock()
+ {
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ if (!_M_initialized)
+ return;
+# endif
+# ifdef _DEBUG
+ _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked");
+ _M_locked = false;
+# endif
+ _M_mtx_impl.unlock();
+ }
+ private:
+ fast_mutex(const fast_mutex&);
+ fast_mutex& operator=(const fast_mutex&);
+ };
+NVWA_NAMESPACE_END
+# elif defined(NVWA_PTHREADS)
# include <pthread.h>
+NVWA_NAMESPACE_BEGIN
/**
* Macro alias to `volatile' semantics. Here it is truly volatile since
* it is in a multi-threaded (POSIX threads) environment.
@@ -118,69 +224,72 @@
*/
class fast_mutex
{
- pthread_mutex_t _M_mtx_impl;
-# if _FAST_MUTEX_CHECK_INITIALIZATION
- bool _M_initialized;
-# endif
-# ifdef _DEBUG
- bool _M_locked;
-# endif
- public:
- fast_mutex()
-# ifdef _DEBUG
- : _M_locked(false)
-# endif
- {
- ::pthread_mutex_init(&_M_mtx_impl, nullptr);
-# if _FAST_MUTEX_CHECK_INITIALIZATION
- _M_initialized = true;
-# endif
- }
- ~fast_mutex()
- {
- _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked");
-# if _FAST_MUTEX_CHECK_INITIALIZATION
- _M_initialized = false;
-# endif
- ::pthread_mutex_destroy(&_M_mtx_impl);
- }
- void lock()
- {
-# if _FAST_MUTEX_CHECK_INITIALIZATION
- if (!_M_initialized)
- return;
-# endif
- ::pthread_mutex_lock(&_M_mtx_impl);
-# ifdef _DEBUG
- // The following assertion should _always_ be true for a
- // real `fast' pthread_mutex. However, this assertion can
- // help sometimes, when people forget to use `-lpthread' and
- // glibc provides an empty implementation. Having this
- // assertion is also more consistent.
- _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked");
- _M_locked = true;
-# endif
- }
- void unlock()
- {
-# if _FAST_MUTEX_CHECK_INITIALIZATION
- if (!_M_initialized)
- return;
-# endif
-# ifdef _DEBUG
- _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked");
- _M_locked = false;
-# endif
- ::pthread_mutex_unlock(&_M_mtx_impl);
- }
- private:
- fast_mutex(const fast_mutex&);
- fast_mutex& operator=(const fast_mutex&);
+ pthread_mutex_t _M_mtx_impl;
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ bool _M_initialized;
+# endif
+# ifdef _DEBUG
+ bool _M_locked;
+# endif
+ public:
+ fast_mutex()
+# ifdef _DEBUG
+ : _M_locked(false)
+# endif
+ {
+ ::pthread_mutex_init(&_M_mtx_impl, NULL);
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ _M_initialized = true;
+# endif
+ }
+ ~fast_mutex()
+ {
+ _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked");
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ _M_initialized = false;
+# endif
+ ::pthread_mutex_destroy(&_M_mtx_impl);
+ }
+ void lock()
+ {
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ if (!_M_initialized)
+ return;
+# endif
+ ::pthread_mutex_lock(&_M_mtx_impl);
+# ifdef _DEBUG
+ // The following assertion should _always_ be true for a
+ // real `fast' pthread_mutex. However, this assertion can
+ // help sometimes, when people forget to use `-lpthread' and
+ // glibc provides an empty implementation. Having this
+ // assertion is also more consistent.
+ _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked");
+ _M_locked = true;
+# endif
+ }
+ void unlock()
+ {
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ if (!_M_initialized)
+ return;
+# endif
+# ifdef _DEBUG
+ _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked");
+ _M_locked = false;
+# endif
+ ::pthread_mutex_unlock(&_M_mtx_impl);
+ }
+ private:
+ fast_mutex(const fast_mutex&);
+ fast_mutex& operator=(const fast_mutex&);
};
-# endif // _PTHREADS
-
-# ifdef _WIN32THREADS
+NVWA_NAMESPACE_END
+# elif defined(NVWA_WIN32THREADS)
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif /* WIN32_LEAN_AND_MEAN */
# include <windows.h>
+NVWA_NAMESPACE_BEGIN
/**
* Macro alias to `volatile' semantics. Here it is truly volatile since
* it is in a multi-threaded (Win32 threads) environment.
@@ -192,63 +301,63 @@
*/
class fast_mutex
{
- CRITICAL_SECTION _M_mtx_impl;
-# if _FAST_MUTEX_CHECK_INITIALIZATION
- bool _M_initialized;
-# endif
-# ifdef _DEBUG
- bool _M_locked;
-# endif
- public:
- fast_mutex()
-# ifdef _DEBUG
- : _M_locked(false)
-# endif
- {
- ::InitializeCriticalSection(&_M_mtx_impl);
-# if _FAST_MUTEX_CHECK_INITIALIZATION
- _M_initialized = true;
-# endif
- }
- ~fast_mutex()
- {
- _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked");
-# if _FAST_MUTEX_CHECK_INITIALIZATION
- _M_initialized = false;
-# endif
- ::DeleteCriticalSection(&_M_mtx_impl);
- }
- void lock()
- {
-# if _FAST_MUTEX_CHECK_INITIALIZATION
- if (!_M_initialized)
- return;
-# endif
- ::EnterCriticalSection(&_M_mtx_impl);
-# ifdef _DEBUG
- _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked");
- _M_locked = true;
-# endif
- }
- void unlock()
- {
-# if _FAST_MUTEX_CHECK_INITIALIZATION
- if (!_M_initialized)
- return;
-# endif
-# ifdef _DEBUG
- _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked");
- _M_locked = false;
-# endif
- ::LeaveCriticalSection(&_M_mtx_impl);
- }
- private:
- fast_mutex(const fast_mutex&);
- fast_mutex& operator=(const fast_mutex&);
+ CRITICAL_SECTION _M_mtx_impl;
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ bool _M_initialized;
+# endif
+# ifdef _DEBUG
+ bool _M_locked;
+# endif
+ public:
+ fast_mutex()
+# ifdef _DEBUG
+ : _M_locked(false)
+# endif
+ {
+ ::InitializeCriticalSection(&_M_mtx_impl);
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ _M_initialized = true;
+# endif
+ }
+ ~fast_mutex()
+ {
+ _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked");
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ _M_initialized = false;
+# endif
+ ::DeleteCriticalSection(&_M_mtx_impl);
+ }
+ void lock()
+ {
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ if (!_M_initialized)
+ return;
+# endif
+ ::EnterCriticalSection(&_M_mtx_impl);
+# ifdef _DEBUG
+ _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked");
+ _M_locked = true;
+# endif
+ }
+ void unlock()
+ {
+# if _FAST_MUTEX_CHECK_INITIALIZATION
+ if (!_M_initialized)
+ return;
+# endif
+# ifdef _DEBUG
+ _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked");
+ _M_locked = false;
+# endif
+ ::LeaveCriticalSection(&_M_mtx_impl);
+ }
+ private:
+ fast_mutex(const fast_mutex&);
+ fast_mutex& operator=(const fast_mutex&);
};
-# endif // _WIN32THREADS
-
-# ifdef _NOTHREADS
+NVWA_NAMESPACE_END
+# elif defined(NVWA_NOTHREADS)
+NVWA_NAMESPACE_BEGIN
/**
* Macro alias to `volatile' semantics. Here it is not truly volatile
* since it is in a single-threaded environment.
@@ -260,56 +369,59 @@
*/
class fast_mutex
{
-# ifdef _DEBUG
- bool _M_locked;
-# endif
- public:
- fast_mutex()
-# ifdef _DEBUG
- : _M_locked(false)
-# endif
- {
- }
- ~fast_mutex()
- {
- _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked");
- }
- void lock()
- {
-# ifdef _DEBUG
- _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked");
- _M_locked = true;
-# endif
- }
- void unlock()
- {
-# ifdef _DEBUG
- _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked");
- _M_locked = false;
-# endif
- }
- private:
- fast_mutex(const fast_mutex&);
- fast_mutex& operator=(const fast_mutex&);
- };
-# endif // _NOTHREADS
-
-/** An acquistion-on-initialization lock class based on fast_mutex. */
-class fast_mutex_autolock
-{
- fast_mutex& _M_mtx;
+# ifdef _DEBUG
+ bool _M_locked;
+# endif
public:
- explicit fast_mutex_autolock(fast_mutex& __mtx) : _M_mtx(__mtx)
+ fast_mutex()
+# ifdef _DEBUG
+ : _M_locked(false)
+# endif
{
- _M_mtx.lock();
}
- ~fast_mutex_autolock()
+ ~fast_mutex()
{
- _M_mtx.unlock();
+ _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked");
+ }
+ void lock()
+ {
+# ifdef _DEBUG
+ _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked");
+ _M_locked = true;
+# endif
+ }
+ void unlock()
+ {
+# ifdef _DEBUG
+ _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked");
+ _M_locked = false;
+# endif
}
private:
- fast_mutex_autolock(const fast_mutex_autolock&);
- fast_mutex_autolock& operator=(const fast_mutex_autolock&);
+ fast_mutex(const fast_mutex&);
+ fast_mutex& operator=(const fast_mutex&);
+ };
+NVWA_NAMESPACE_END
+# endif // Definition of class fast_mutex
+
+NVWA_NAMESPACE_BEGIN
+/** RAII lock class for fast_mutex. */
+class fast_mutex_autolock
+{
+ fast_mutex& _M_mtx;
+public:
+ explicit fast_mutex_autolock(fast_mutex& mtx) : _M_mtx(mtx)
+ {
+ _M_mtx.lock();
+ }
+ ~fast_mutex_autolock()
+ {
+ _M_mtx.unlock();
+ }
+private:
+ fast_mutex_autolock(const fast_mutex_autolock&);
+ fast_mutex_autolock& operator=(const fast_mutex_autolock&);
};
+NVWA_NAMESPACE_END
-#endif // M_FAST_MUTEX_H
+#endif // NVWA_FAST_MUTEX_H
diff --git a/src/debug/static_assert.h b/src/debug/static_assert.h
index 04833d844..49248611d 100644
--- a/src/debug/static_assert.h
+++ b/src/debug/static_assert.h
@@ -2,7 +2,7 @@
// vim:tabstop=4:shiftwidth=4:expandtab:
/*
- * Copyright (C) 2004-2008 Wu Yongwei <adah at users dot sourceforge dot net>
+ * Copyright (C) 2004-2013 Wu Yongwei <adah at users dot sourceforge dot net>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
@@ -24,30 +24,39 @@
* This file is part of Stones of Nvwa:
* http://sourceforge.net/projects/nvwa
*
- * original version changed for ManaPlus
- *
- * Copyright (C) 2011-2017 The ManaPlus Developers
*/
/**
- * @file static_assert.h
- *
- * Template class to check validity during compilation time (adapted from Loki).
+ * @file static_assert.h
*
- * @version 1.2, 2005/11/22
- * @author Wu Yongwei
+ * Template class to check validity duing compile time (adapted from Loki).
*
+ * @date 2013-09-07
*/
#ifndef STATIC_ASSERT
-template <bool> struct __nvwa_compile_time_error;
-template <> struct __nvwa_compile_time_error<true> {};
+#include "debug/c++11.h"
+
+#if HAVE_CXX11_STATIC_ASSERT
+
+#define STATIC_ASSERT(_Expr, _Msg) static_assert(_Expr, #_Msg)
+
+#else
+
+namespace nvwa {
+
+template <bool> struct compile_time_error;
+template <> struct compile_time_error<true> {};
#define STATIC_ASSERT(_Expr, _Msg) \
{ \
- __nvwa_compile_time_error<((_Expr) != 0)> ERROR_##_Msg; \
+ nvwa::compile_time_error<((_Expr) != 0)> ERROR_##_Msg; \
(void)ERROR_##_Msg; \
}
-#endif // STATIC_ASSERT
+}
+
+#endif // HAVE_CXX11_STATIC_ASSERT
+
+#endif // STATIC_ASSERT