From 8962542b93ec0b18c118933adf8921c6d13864e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ozan=20=C5=9Eener?= Date: Wed, 24 Mar 2021 11:10:58 +0100 Subject: [PATCH 01/13] Allow running off the main thread on macOS This still does not allow calling Ultralight from different threads, but with this change it is now possible to use a separate worker thread for interacting with Ultralight. --- Source/WTF/wtf/Platform.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/WTF/wtf/Platform.h b/Source/WTF/wtf/Platform.h index aa3c38ff1..fd2b996ec 100644 --- a/Source/WTF/wtf/Platform.h +++ b/Source/WTF/wtf/Platform.h @@ -693,7 +693,8 @@ #define HAVE_TM_GMTOFF 1 #define HAVE_TM_ZONE 1 #define HAVE_TIMEGM 1 -#define HAVE_PTHREAD_MAIN_NP 1 +// Allows running off the main thread on macOS +#define HAVE_PTHREAD_MAIN_NP 0 #if CPU(X86_64) || CPU(ARM64) #define HAVE_INT128_T 1 From 38bd2d86a7d9648e89a85ab48815d934a1800ae0 Mon Sep 17 00:00:00 2001 From: 19h Date: Tue, 31 Aug 2021 00:22:31 +0200 Subject: [PATCH 02/13] Fix WebKitCompilerFlags erroring when CMAKE_CXX_FLAGS is empty --- Source/cmake/WebKitCompilerFlags.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/cmake/WebKitCompilerFlags.cmake b/Source/cmake/WebKitCompilerFlags.cmake index c5fa4c3ea..3f82715b6 100644 --- a/Source/cmake/WebKitCompilerFlags.cmake +++ b/Source/cmake/WebKitCompilerFlags.cmake @@ -228,7 +228,7 @@ if (COMPILER_IS_GCC_OR_CLANG) endif () if (NOT MSVC) - string(REGEX MATCHALL "-fsanitize=[^ ]*" ENABLED_COMPILER_SANITIZERS ${CMAKE_CXX_FLAGS}) + string(REGEX MATCHALL "-fsanitize=[^ ]*" ENABLED_COMPILER_SANITIZERS "${CMAKE_CXX_FLAGS}") endif () if (UNIX AND NOT APPLE AND NOT ENABLED_COMPILER_SANITIZERS) From 02906e65b02243ec606dfba85912e81a9513b1e6 Mon Sep 17 00:00:00 2001 From: p0358 Date: Mon, 28 Nov 2022 12:14:09 +0100 Subject: [PATCH 03/13] Make context menu work in web inspector --- Source/WebInspectorUI/UserInterface/Base/Main.js | 14 ++++++++++++++ .../UserInterface/Views/ContextMenu.js | 9 +++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Source/WebInspectorUI/UserInterface/Base/Main.js b/Source/WebInspectorUI/UserInterface/Base/Main.js index f244434a7..e57be6b80 100644 --- a/Source/WebInspectorUI/UserInterface/Base/Main.js +++ b/Source/WebInspectorUI/UserInterface/Base/Main.js @@ -1717,6 +1717,20 @@ WI._mouseDown = function(event) { if (WI.toolbar.element.contains(event.target)) WI._toolbarMouseDown(event); + + if (event.button === 2) { // right-click + const contextMenuEvent = new MouseEvent("contextmenu", { + view: window, + bubbles: true, + cancelable: true, + screenX: event.pageX, + screenY: event.pageY, + clientX: event.pageX, + clientY: event.pageY, + ...event + }); + event.target.dispatchEvent(contextMenuEvent); + } }; WI._mouseMoved = function(event) diff --git a/Source/WebInspectorUI/UserInterface/Views/ContextMenu.js b/Source/WebInspectorUI/UserInterface/Views/ContextMenu.js index 95f7bee62..51a550070 100644 --- a/Source/WebInspectorUI/UserInterface/Views/ContextMenu.js +++ b/Source/WebInspectorUI/UserInterface/Views/ContextMenu.js @@ -205,7 +205,7 @@ WI.ContextMenu = class ContextMenu extends WI.ContextSubMenuItem this._event.target.addEventListener("contextmenu", this, true); InspectorFrontendHost.dispatchEventAsContextMenuEvent(this._event); } else - InspectorFrontendHost.showContextMenu(this._event, menuObject); + this._showSoftContextMenu(this._event, menuObject); } if (this._event) @@ -227,7 +227,7 @@ WI.ContextMenu = class ContextMenu extends WI.ContextSubMenuItem callback(this); this._event.target.removeEventListener("contextmenu", this, true); - InspectorFrontendHost.showContextMenu(event, this._menuObject); + this._showSoftContextMenu(event, this._menuObject); this._menuObject = null; event.stopImmediatePropagation(); @@ -251,6 +251,11 @@ WI.ContextMenu = class ContextMenu extends WI.ContextSubMenuItem if (this._handlers[id]) this._handlers[id].call(this); } + + _showSoftContextMenu(event, menuObject) + { + new WI.SoftContextMenu(menuObject).show(event); + } }; WI.ContextMenu.ProposedMenuSymbol = Symbol("context-menu-proposed-menu"); From 71c749b6acd29ea23b1a82d10878f1dbb62e0fb2 Mon Sep 17 00:00:00 2001 From: Adam Simmons Date: Thu, 20 Apr 2023 13:17:59 -0500 Subject: [PATCH 04/13] Annotate more functions for performance profiling. --- Source/JavaScriptCore/config.h | 12 ++++++++++++ Source/JavaScriptCore/interpreter/Interpreter.cpp | 6 ++++++ Source/JavaScriptCore/jit/JITCodeInlines.h | 1 + Source/JavaScriptCore/runtime/CallData.cpp | 1 + Source/JavaScriptCore/runtime/ScriptExecutable.cpp | 1 + Source/WebCore/bindings/js/ScheduledAction.cpp | 3 +++ Source/WebCore/config.h | 10 ++++++++++ 7 files changed, 34 insertions(+) diff --git a/Source/JavaScriptCore/config.h b/Source/JavaScriptCore/config.h index fa1b8a97f..4995b0e4f 100644 --- a/Source/JavaScriptCore/config.h +++ b/Source/JavaScriptCore/config.h @@ -36,6 +36,18 @@ #undef new #undef delete #include + +#if USE(ULTRALIGHT) +#include +#include +#else +#define ProfileAlloc(ptr, size, name) +#define ProfileFree(ptr, name) +#define ProfiledZone +#define ProfiledMemoryZone(tag) #endif + +#endif // #ifdef __cplusplus + #include diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp index c1f2f1f44..9cf9cf946 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.cpp +++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp @@ -661,6 +661,7 @@ void Interpreter::notifyDebuggerOfExceptionToBeThrown(VM& vm, CallFrame* callFra JSValue Interpreter::executeProgram(const SourceCode& source, CallFrame* callFrame, JSObject* thisObj) { + ProfiledZone; JSScope* scope = thisObj->globalObject()->globalScope(); VM& vm = *scope->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -848,6 +849,7 @@ JSValue Interpreter::executeProgram(const SourceCode& source, CallFrame* callFra JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) { + ProfiledZone; VM& vm = callFrame->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -913,6 +915,7 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args, JSValue newTarget) { + ProfiledZone; VM& vm = callFrame->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -987,6 +990,7 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, ProtoCallFrame* protoCallFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope, const ArgList& args) { + ProfiledZone; VM& vm = *scope->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); throwScope.assertNoException(); @@ -1012,6 +1016,7 @@ CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionE JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, JSScope* scope) { + ProfiledZone; VM& vm = *scope->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -1161,6 +1166,7 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue JSValue Interpreter::executeModuleProgram(ModuleProgramExecutable* executable, CallFrame* callFrame, JSModuleEnvironment* scope) { + ProfiledZone; VM& vm = *scope->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); diff --git a/Source/JavaScriptCore/jit/JITCodeInlines.h b/Source/JavaScriptCore/jit/JITCodeInlines.h index 696524d10..98d5df822 100644 --- a/Source/JavaScriptCore/jit/JITCodeInlines.h +++ b/Source/JavaScriptCore/jit/JITCodeInlines.h @@ -32,6 +32,7 @@ namespace JSC { ALWAYS_INLINE JSValue JITCode::execute(VM* vm, ProtoCallFrame* protoCallFrame) { + ProfiledZone; auto scope = DECLARE_THROW_SCOPE(*vm); void* entryAddress; entryAddress = addressForCall(MustCheckArity).executableAddress(); diff --git a/Source/JavaScriptCore/runtime/CallData.cpp b/Source/JavaScriptCore/runtime/CallData.cpp index 3c23304c0..af988ae4c 100644 --- a/Source/JavaScriptCore/runtime/CallData.cpp +++ b/Source/JavaScriptCore/runtime/CallData.cpp @@ -56,6 +56,7 @@ JSValue call(ExecState* exec, JSValue functionObject, JSValue thisValue, const A JSValue call(ExecState* exec, JSValue functionObject, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) { + ProfiledZone; ProfiledMemoryZone(MemoryTag::JavaScript); VM& vm = exec->vm(); ASSERT(callType == CallType::JS || callType == CallType::Host); diff --git a/Source/JavaScriptCore/runtime/ScriptExecutable.cpp b/Source/JavaScriptCore/runtime/ScriptExecutable.cpp index a2917f282..1b2c67d56 100644 --- a/Source/JavaScriptCore/runtime/ScriptExecutable.cpp +++ b/Source/JavaScriptCore/runtime/ScriptExecutable.cpp @@ -410,6 +410,7 @@ static void setupJIT(VM& vm, CodeBlock* codeBlock) Exception* ScriptExecutable::prepareForExecutionImpl( VM& vm, JSFunction* function, JSScope* scope, CodeSpecializationKind kind, CodeBlock*& resultCodeBlock) { + ProfiledZone; ProfiledMemoryZone(MemoryTag::JavaScript_Bytecode); auto throwScope = DECLARE_THROW_SCOPE(vm); DeferGCForAWhile deferGC(vm.heap); diff --git a/Source/WebCore/bindings/js/ScheduledAction.cpp b/Source/WebCore/bindings/js/ScheduledAction.cpp index cc999daeb..2390e2f32 100644 --- a/Source/WebCore/bindings/js/ScheduledAction.cpp +++ b/Source/WebCore/bindings/js/ScheduledAction.cpp @@ -90,6 +90,7 @@ void ScheduledAction::execute(ScriptExecutionContext& context) void ScheduledAction::executeFunctionInContext(JSGlobalObject* globalObject, JSValue thisValue, ScriptExecutionContext& context) { + ProfiledZone; ASSERT(m_function); VM& vm = context.vm(); JSLockHolder lock(vm); @@ -125,6 +126,7 @@ void ScheduledAction::executeFunctionInContext(JSGlobalObject* globalObject, JSV void ScheduledAction::execute(Document& document) { + ProfiledZone; JSDOMWindow* window = toJSDOMWindow(document.frame(), m_isolatedWorld); if (!window) return; @@ -141,6 +143,7 @@ void ScheduledAction::execute(Document& document) void ScheduledAction::execute(WorkerGlobalScope& workerGlobalScope) { + ProfiledZone; // In a Worker, the execution should always happen on a worker thread. ASSERT(workerGlobalScope.thread().thread() == &Thread::current()); diff --git a/Source/WebCore/config.h b/Source/WebCore/config.h index 0d77ad834..1438f47c2 100644 --- a/Source/WebCore/config.h +++ b/Source/WebCore/config.h @@ -76,6 +76,16 @@ #include +#if USE(ULTRALIGHT) +#include +#include +#else +#define ProfileAlloc(ptr, size, name) +#define ProfileFree(ptr, name) +#define ProfiledZone +#define ProfiledMemoryZone(tag) +#endif + #endif #include From 63f5d5f910acea9ce5e938e381cf931dab639a4a Mon Sep 17 00:00:00 2001 From: Adam Simmons Date: Thu, 20 Apr 2023 13:23:11 -0500 Subject: [PATCH 05/13] Update mimalloc to 2.1.1 --- Source/mimalloc/.gitattributes | 1 + Source/mimalloc/.gitignore | 1 + Source/mimalloc/CMakeLists.txt | 460 +++- Source/mimalloc/azure-pipelines.yml | 35 +- Source/mimalloc/bin/mimalloc-redirect.dll | Bin 55808 -> 68096 bytes Source/mimalloc/bin/mimalloc-redirect.lib | Bin 2874 -> 2874 bytes Source/mimalloc/bin/mimalloc-redirect32.dll | Bin 39424 -> 41984 bytes Source/mimalloc/bin/mimalloc-redirect32.lib | Bin 2928 -> 2928 bytes Source/mimalloc/bin/minject.exe | Bin 0 -> 20992 bytes Source/mimalloc/bin/minject32.exe | Bin 0 -> 18432 bytes Source/mimalloc/cmake/JoinPaths.cmake | 23 + .../cmake/mimalloc-config-version.cmake | 5 +- Source/mimalloc/cmake/mimalloc-config.cmake | 19 +- Source/mimalloc/doc/doxyfile | 281 ++- Source/mimalloc/doc/ds-logo.jpg | Bin 168349 -> 181497 bytes Source/mimalloc/doc/ds-logo.png | Bin 0 -> 121150 bytes Source/mimalloc/doc/mimalloc-doc.h | 84 +- Source/mimalloc/doc/spades-logo.png | Bin 0 -> 34583 bytes Source/mimalloc/doc/unreal-logo.svg | 43 + .../ide/vs2017/mimalloc-override-test.vcxproj | 4 +- .../ide/vs2017/mimalloc-override.vcxproj | 21 +- .../ide/vs2017/mimalloc-test-stress.vcxproj | 4 +- .../mimalloc/ide/vs2017/mimalloc-test.vcxproj | 6 +- Source/mimalloc/ide/vs2017/mimalloc.sln | 2 +- Source/mimalloc/ide/vs2017/mimalloc.vcxproj | 23 +- .../ide/vs2019/mimalloc-override.vcxproj | 19 +- Source/mimalloc/ide/vs2019/mimalloc.sln | 162 +- Source/mimalloc/ide/vs2019/mimalloc.vcxproj | 31 +- .../ide/vs2022/mimalloc-override-test.vcxproj | 190 ++ .../ide/vs2022/mimalloc-override.vcxproj | 270 +++ .../ide/vs2022/mimalloc-test-api.vcxproj | 155 ++ .../ide/vs2022/mimalloc-test-stress.vcxproj | 159 ++ .../mimalloc/ide/vs2022/mimalloc-test.vcxproj | 158 ++ Source/mimalloc/ide/vs2022/mimalloc.sln | 81 + Source/mimalloc/ide/vs2022/mimalloc.vcxproj | 258 +++ Source/mimalloc/include/mimalloc-new-delete.h | 26 +- Source/mimalloc/include/mimalloc-override.h | 1 + Source/mimalloc/include/mimalloc.h | 191 +- .../{mimalloc-atomic.h => mimalloc/atomic.h} | 29 +- .../internal.h} | 565 ++--- Source/mimalloc/include/mimalloc/prim.h | 311 +++ Source/mimalloc/include/mimalloc/track.h | 147 ++ .../{mimalloc-types.h => mimalloc/types.h} | 316 ++- Source/mimalloc/mimalloc.pc.in | 11 + Source/mimalloc/readme.md | 280 ++- Source/mimalloc/src/alloc-aligned.c | 250 ++- Source/mimalloc/src/alloc-override-osx.c | 281 --- Source/mimalloc/src/alloc-override.c | 213 +- Source/mimalloc/src/alloc-posix.c | 71 +- Source/mimalloc/src/alloc.c | 710 +++--- Source/mimalloc/src/arena.c | 346 ++- Source/mimalloc/src/bitmap.c | 147 +- Source/mimalloc/src/bitmap.h | 23 +- Source/mimalloc/src/heap.c | 186 +- Source/mimalloc/src/init.c | 406 ++-- Source/mimalloc/src/options.c | 366 ++-- Source/mimalloc/src/os.c | 1058 ++------- Source/mimalloc/src/page-queue.c | 40 +- Source/mimalloc/src/page.c | 261 ++- .../src/prim/osx/alloc-override-zone.c | 458 ++++ Source/mimalloc/src/prim/osx/prim.c | 9 + Source/mimalloc/src/prim/prim.c | 24 + Source/mimalloc/src/prim/readme.md | 9 + Source/mimalloc/src/prim/unix/prim.c | 838 ++++++++ Source/mimalloc/src/prim/wasi/prim.c | 265 +++ .../src/prim/windows/etw-mimalloc.wprp | 61 + Source/mimalloc/src/prim/windows/etw.h | 905 ++++++++ Source/mimalloc/src/prim/windows/etw.man | Bin 0 -> 3926 bytes Source/mimalloc/src/prim/windows/prim.c | 607 ++++++ Source/mimalloc/src/prim/windows/readme.md | 17 + Source/mimalloc/src/random.c | 147 +- Source/mimalloc/src/region.c | 146 +- Source/mimalloc/src/segment-cache.c | 423 ++++ Source/mimalloc/src/segment.c | 1915 ++++++++++------- Source/mimalloc/src/static.c | 29 +- Source/mimalloc/src/stats.c | 269 +-- Source/mimalloc/test/CMakeLists.txt | 14 +- Source/mimalloc/test/main-override-static.c | 245 ++- Source/mimalloc/test/main-override.c | 8 +- Source/mimalloc/test/main-override.cpp | 254 ++- Source/mimalloc/test/test-api-fill.c | 343 +++ Source/mimalloc/test/test-api.c | 248 ++- Source/mimalloc/test/test-stress.c | 37 +- Source/mimalloc/test/test-wrong.c | 92 + Source/mimalloc/test/testhelper.h | 49 + 85 files changed, 11731 insertions(+), 4411 deletions(-) create mode 100644 Source/mimalloc/bin/minject.exe create mode 100644 Source/mimalloc/bin/minject32.exe create mode 100644 Source/mimalloc/cmake/JoinPaths.cmake create mode 100644 Source/mimalloc/doc/ds-logo.png create mode 100644 Source/mimalloc/doc/spades-logo.png create mode 100644 Source/mimalloc/doc/unreal-logo.svg create mode 100644 Source/mimalloc/ide/vs2022/mimalloc-override-test.vcxproj create mode 100644 Source/mimalloc/ide/vs2022/mimalloc-override.vcxproj create mode 100644 Source/mimalloc/ide/vs2022/mimalloc-test-api.vcxproj create mode 100644 Source/mimalloc/ide/vs2022/mimalloc-test-stress.vcxproj create mode 100644 Source/mimalloc/ide/vs2022/mimalloc-test.vcxproj create mode 100644 Source/mimalloc/ide/vs2022/mimalloc.sln create mode 100644 Source/mimalloc/ide/vs2022/mimalloc.vcxproj rename Source/mimalloc/include/{mimalloc-atomic.h => mimalloc/atomic.h} (94%) rename Source/mimalloc/include/{mimalloc-internal.h => mimalloc/internal.h} (64%) create mode 100644 Source/mimalloc/include/mimalloc/prim.h create mode 100644 Source/mimalloc/include/mimalloc/track.h rename Source/mimalloc/include/{mimalloc-types.h => mimalloc/types.h} (61%) create mode 100644 Source/mimalloc/mimalloc.pc.in delete mode 100644 Source/mimalloc/src/alloc-override-osx.c create mode 100644 Source/mimalloc/src/prim/osx/alloc-override-zone.c create mode 100644 Source/mimalloc/src/prim/osx/prim.c create mode 100644 Source/mimalloc/src/prim/prim.c create mode 100644 Source/mimalloc/src/prim/readme.md create mode 100644 Source/mimalloc/src/prim/unix/prim.c create mode 100644 Source/mimalloc/src/prim/wasi/prim.c create mode 100644 Source/mimalloc/src/prim/windows/etw-mimalloc.wprp create mode 100644 Source/mimalloc/src/prim/windows/etw.h create mode 100644 Source/mimalloc/src/prim/windows/etw.man create mode 100644 Source/mimalloc/src/prim/windows/prim.c create mode 100644 Source/mimalloc/src/prim/windows/readme.md create mode 100644 Source/mimalloc/src/segment-cache.c create mode 100644 Source/mimalloc/test/test-api-fill.c create mode 100644 Source/mimalloc/test/test-wrong.c create mode 100644 Source/mimalloc/test/testhelper.h diff --git a/Source/mimalloc/.gitattributes b/Source/mimalloc/.gitattributes index 1534e7787..0332e0315 100644 --- a/Source/mimalloc/.gitattributes +++ b/Source/mimalloc/.gitattributes @@ -9,3 +9,4 @@ *.patch binary *.dll binary *.lib binary +*.exe binary diff --git a/Source/mimalloc/.gitignore b/Source/mimalloc/.gitignore index 3639d3241..f8b7f5eb8 100644 --- a/Source/mimalloc/.gitignore +++ b/Source/mimalloc/.gitignore @@ -3,6 +3,7 @@ ide/vs20??/*.opendb ide/vs20??/*.user ide/vs20??/*.vcxproj.filters ide/vs20??/.vs +ide/vs20??/VTune* out/ docs/ *.zip diff --git a/Source/mimalloc/CMakeLists.txt b/Source/mimalloc/CMakeLists.txt index fee44f759..35d5d6509 100644 --- a/Source/mimalloc/CMakeLists.txt +++ b/Source/mimalloc/CMakeLists.txt @@ -1,44 +1,82 @@ +cmake_minimum_required(VERSION 3.13) +project(libmimalloc C CXX) + set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) option(MI_SECURE "Use full security mitigations (like guard pages, allocation randomization, double-free mitigation, and free-list corruption detection)" OFF) option(MI_DEBUG_FULL "Use full internal heap invariant checking in DEBUG mode (expensive)" OFF) -option(MI_PADDING "Enable padding to detect heap block overflow (used only in DEBUG mode)" OFF) +option(MI_PADDING "Enable padding to detect heap block overflow (always on in DEBUG or SECURE mode, or with Valgrind/ASAN)" OFF) +option(MI_OVERRIDE "Override the standard malloc interface (e.g. define entry points for malloc() etc)" ON) option(MI_XMALLOC "Enable abort() call on memory allocation failure by default" OFF) option(MI_SHOW_ERRORS "Show error and warning messages by default (only enabled by default in DEBUG mode)" OFF) +option(MI_TRACK_VALGRIND "Compile with Valgrind support (adds a small overhead)" OFF) +option(MI_TRACK_ASAN "Compile with address sanitizer support (adds a small overhead)" OFF) +option(MI_TRACK_ETW "Compile with Windows event tracing (ETW) support (adds a small overhead)" OFF) option(MI_USE_CXX "Use the C++ compiler to compile the library (instead of the C compiler)" OFF) option(MI_SEE_ASM "Generate assembly files" OFF) -option(MI_INTERPOSE "Use interpose to override standard malloc on macOS" OFF) -option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" OFF) +option(MI_OSX_INTERPOSE "Use interpose to override standard malloc on macOS" ON) +option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" ON) +option(MI_WIN_REDIRECT "Use redirection module ('mimalloc-redirect') on Windows if compiling mimalloc as a DLL" ON) option(MI_LOCAL_DYNAMIC_TLS "Use slightly slower, dlopen-compatible TLS mechanism (Unix)" OFF) +option(MI_BUILD_SHARED "Build shared library" ON) +option(MI_BUILD_STATIC "Build static library" ON) +option(MI_BUILD_OBJECT "Build object library" ON) +option(MI_BUILD_TESTS "Build test executables" ON) option(MI_DEBUG_TSAN "Build with thread sanitizer (needs clang)" OFF) option(MI_DEBUG_UBSAN "Build with undefined-behavior sanitizer (needs clang++)" OFF) +option(MI_SKIP_COLLECT_ON_EXIT "Skip collecting memory on program exit" OFF) +option(MI_NO_PADDING "Force no use of padding even in DEBUG mode etc." OFF) + +# deprecated options option(MI_CHECK_FULL "Use full internal invariant checking in DEBUG mode (deprecated, use MI_DEBUG_FULL instead)" OFF) +option(MI_INSTALL_TOPLEVEL "Install directly into $CMAKE_INSTALL_PREFIX instead of PREFIX/lib/mimalloc-version (deprecated)" OFF) +option(MI_USE_LIBATOMIC "Explicitly link with -latomic (on older systems) (deprecated and detected automatically)" OFF) -set_property(DIRECTORY . PROPERTY FOLDER "mimalloc") +include(CheckIncludeFiles) +include(GNUInstallDirs) +include("cmake/mimalloc-config-version.cmake") -set(mimalloc_SOURCES - src/stats.c - src/random.c - src/os.c - src/bitmap.c - src/arena.c - src/region.c - src/segment.c - src/page.c +set(mi_sources src/alloc.c src/alloc-aligned.c src/alloc-posix.c + src/arena.c + src/bitmap.c src/heap.c - src/options.c src/init.c -) + src/options.c + src/os.c + src/page.c + src/random.c + src/segment.c + src/segment-cache.c + src/stats.c + src/prim/prim.c) + +set(mi_cflags "") +set(mi_libraries "") + +# ----------------------------------------------------------------------------- +# Convenience: set default build type depending on the build directory +# ----------------------------------------------------------------------------- + +message(STATUS "") +if (NOT CMAKE_BUILD_TYPE) + if ("${CMAKE_BINARY_DIR}" MATCHES ".*(D|d)ebug$" OR MI_DEBUG_FULL) + message(STATUS "No build type selected, default to: Debug") + set(CMAKE_BUILD_TYPE "Debug") + else() + message(STATUS "No build type selected, default to: Release") + set(CMAKE_BUILD_TYPE "Release") + endif() +endif() + +if("${CMAKE_BINARY_DIR}" MATCHES ".*(S|s)ecure$") + message(STATUS "Default to secure build") + set(MI_SECURE "ON") +endif() -set(mimalloc_PUBLIC_HEADERS - include/mimalloc.h - include/mimalloc-override.h - include/mimalloc-new-delete.h -) # ----------------------------------------------------------------------------- # Process options @@ -48,9 +86,99 @@ if(CMAKE_C_COMPILER_ID MATCHES "MSVC|Intel") set(MI_USE_CXX "ON") endif() +if(MI_OVERRIDE) + message(STATUS "Override standard malloc (MI_OVERRIDE=ON)") + if(APPLE) + if(MI_OSX_ZONE) + # use zone's on macOS + message(STATUS " Use malloc zone to override malloc (MI_OSX_ZONE=ON)") + list(APPEND mi_sources src/prim/osx/alloc-override-zone.c) + list(APPEND mi_defines MI_OSX_ZONE=1) + if (NOT MI_OSX_INTERPOSE) + message(STATUS " WARNING: zone overriding usually also needs interpose (use -DMI_OSX_INTERPOSE=ON)") + endif() + endif() + if(MI_OSX_INTERPOSE) + # use interpose on macOS + message(STATUS " Use interpose to override malloc (MI_OSX_INTERPOSE=ON)") + list(APPEND mi_defines MI_OSX_INTERPOSE=1) + if (NOT MI_OSX_ZONE) + message(STATUS " WARNING: interpose usually also needs zone overriding (use -DMI_OSX_INTERPOSE=ON)") + endif() + endif() + if(MI_USE_CXX AND MI_OSX_INTERPOSE) + message(STATUS " WARNING: if dynamically overriding malloc/free, it is more reliable to build mimalloc as C code (use -DMI_USE_CXX=OFF)") + endif() + endif() +endif() + +if(WIN32) + if (MI_WIN_REDIRECT) + if (MSVC_C_ARCHITECTURE_ID MATCHES "ARM") + message(STATUS "Cannot use redirection on Windows ARM (MI_WIN_REDIRECT=OFF)") + set(MI_WIN_REDIRECT OFF) + endif() + endif() + if (NOT MI_WIN_REDIRECT) + # use a negative define for backward compatibility + list(APPEND mi_defines MI_WIN_NOREDIRECT=1) + endif() +endif() + if(MI_SECURE) message(STATUS "Set full secure build (MI_SECURE=ON)") - list(APPEND mi_defines MI_SECURE=4) + list(APPEND mi_defines MI_SECURE=4) +endif() + +if(MI_TRACK_VALGRIND) + CHECK_INCLUDE_FILES("valgrind/valgrind.h;valgrind/memcheck.h" MI_HAS_VALGRINDH) + if (NOT MI_HAS_VALGRINDH) + set(MI_TRACK_VALGRIND OFF) + message(WARNING "Cannot find the 'valgrind/valgrind.h' and 'valgrind/memcheck.h' -- install valgrind first") + message(STATUS "Compile **without** Valgrind support (MI_TRACK_VALGRIND=OFF)") + else() + message(STATUS "Compile with Valgrind support (MI_TRACK_VALGRIND=ON)") + list(APPEND mi_defines MI_TRACK_VALGRIND=1) + endif() +endif() + +if(MI_TRACK_ASAN) + if (APPLE AND MI_OVERRIDE) + set(MI_TRACK_ASAN OFF) + message(WARNING "Cannot enable address sanitizer support on macOS if MI_OVERRIDE is ON (MI_TRACK_ASAN=OFF)") + endif() + if (MI_TRACK_VALGRIND) + set(MI_TRACK_ASAN OFF) + message(WARNING "Cannot enable address sanitizer support with also Valgrind support enabled (MI_TRACK_ASAN=OFF)") + endif() + if(MI_TRACK_ASAN) + CHECK_INCLUDE_FILES("sanitizer/asan_interface.h" MI_HAS_ASANH) + if (NOT MI_HAS_ASANH) + set(MI_TRACK_ASAN OFF) + message(WARNING "Cannot find the 'sanitizer/asan_interface.h' -- install address sanitizer support first") + message(STATUS "Compile **without** address sanitizer support (MI_TRACK_ASAN=OFF)") + else() + message(STATUS "Compile with address sanitizer support (MI_TRACK_ASAN=ON)") + list(APPEND mi_defines MI_TRACK_ASAN=1) + list(APPEND mi_cflags -fsanitize=address) + list(APPEND mi_libraries -fsanitize=address) + endif() + endif() +endif() + +if(MI_TRACK_ETW) + if(NOT WIN32) + set(MI_TRACK_ETW OFF) + message(WARNING "Can only enable ETW support on Windows (MI_TRACK_ETW=OFF)") + endif() + if (MI_TRACK_VALGRIND OR MI_TRACK_ASAN) + set(MI_TRACK_ETW OFF) + message(WARNING "Cannot enable ETW support with also Valgrind or ASAN support enabled (MI_TRACK_ETW=OFF)") + endif() + if(MI_TRACK_ETW) + message(STATUS "Compile with Windows event tracing support (MI_TRACK_ETW=ON)") + list(APPEND mi_defines MI_TRACK_ETW=1) + endif() endif() if(MI_SEE_ASM) @@ -63,14 +191,24 @@ if(MI_CHECK_FULL) set(MI_DEBUG_FULL "ON") endif() +if (MI_SKIP_COLLECT_ON_EXIT) + message(STATUS "Skip collecting memory on program exit (MI_SKIP_COLLECT_ON_EXIT=ON)") + list(APPEND mi_defines MI_SKIP_COLLECT_ON_EXIT=1) +endif() + if(MI_DEBUG_FULL) message(STATUS "Set debug level to full internal invariant checking (MI_DEBUG_FULL=ON)") list(APPEND mi_defines MI_DEBUG=3) # full invariant checking endif() -if(NOT MI_PADDING) - message(STATUS "Disable padding of heap blocks in debug mode (MI_PADDING=OFF)") +if(MI_NO_PADDING) + message(STATUS "Suppress any padding of heap blocks (MI_NO_PADDING=ON)") list(APPEND mi_defines MI_PADDING=0) +else() + if(MI_PADDING) + message(STATUS "Enable explicit padding of heap blocks (MI_PADDING=ON)") + list(APPEND mi_defines MI_PADDING=1) + endif() endif() if(MI_XMALLOC) @@ -88,47 +226,52 @@ if(MI_DEBUG_TSAN) message(STATUS "Build with thread sanitizer (MI_DEBUG_TSAN=ON)") list(APPEND mi_defines MI_TSAN=1) list(APPEND mi_cflags -fsanitize=thread -g -O1) - list(APPEND CMAKE_EXE_LINKER_FLAGS -fsanitize=thread) + list(APPEND mi_libraries -fsanitize=thread) else() - message(WARNING "Can only use thread sanitizer with clang (MI_DEBUG_TSAN=ON but ignored)") - endif() + message(WARNING "Can only use thread sanitizer with clang (MI_DEBUG_TSAN=ON but ignored)") + endif() endif() if(MI_DEBUG_UBSAN) - if(CMAKE_BUILD_TYPE MATCHES "Debug") + if(CMAKE_BUILD_TYPE MATCHES "Debug") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") message(STATUS "Build with undefined-behavior sanitizer (MI_DEBUG_UBSAN=ON)") - list(APPEND mi_cflags -fsanitize=undefined -g) - list(APPEND CMAKE_EXE_LINKER_FLAGS -fsanitize=undefined) + list(APPEND mi_cflags -fsanitize=undefined -g -fno-sanitize-recover=undefined) + list(APPEND mi_libraries -fsanitize=undefined) if (NOT MI_USE_CXX) message(STATUS "(switch to use C++ due to MI_DEBUG_UBSAN)") set(MI_USE_CXX "ON") endif() else() - message(WARNING "Can only use undefined-behavior sanitizer with clang++ (MI_DEBUG_UBSAN=ON but ignored)") - endif() + message(WARNING "Can only use undefined-behavior sanitizer with clang++ (MI_DEBUG_UBSAN=ON but ignored)") + endif() else() - message(WARNING "Can only use thread sanitizer with a debug build (CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})") + message(WARNING "Can only use thread sanitizer with a debug build (CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE})") endif() endif() if(MI_USE_CXX) message(STATUS "Use the C++ compiler to compile (MI_USE_CXX=ON)") set_source_files_properties(${mi_sources} PROPERTIES LANGUAGE CXX ) - set_source_files_properties(src/static.c test/test-api.c test/test-stress PROPERTIES LANGUAGE CXX ) + set_source_files_properties(src/static.c test/test-api.c test/test-api-fill test/test-stress PROPERTIES LANGUAGE CXX ) if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang|Clang") list(APPEND mi_cflags -Wno-deprecated) endif() - if(CMAKE_CXX_COMPILER_ID MATCHES "Intel") + if(CMAKE_CXX_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM") list(APPEND mi_cflags -Kc++) endif() endif() +if(CMAKE_SYSTEM_NAME MATCHES "Haiku") + SET(CMAKE_INSTALL_LIBDIR ~/config/non-packaged/lib) + SET(CMAKE_INSTALL_INCLUDEDIR ~/config/non-packaged/headers) + endif() + # Compiler flags if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU") list(APPEND mi_cflags -Wall -Wextra -Wno-unknown-pragmas -fvisibility=hidden) - if(CMAKE_C_COMPILER_ID MATCHES "GNU") - list(APPEND mi_cflags -Wno-invalid-memory-model) + if(NOT MI_USE_CXX) + list(APPEND mi_cflags -Wstrict-prototypes) endif() if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang") list(APPEND mi_cflags -Wpedantic -Wno-static-in-inline) @@ -145,28 +288,38 @@ if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel" AND NOT CMAKE_SYSTEM else() list(APPEND mi_cflags -ftls-model=initial-exec) endif() + if(MI_OVERRIDE) + list(APPEND mi_cflags -fno-builtin-malloc) + endif() endif() if (MSVC AND MSVC_VERSION GREATER_EQUAL 1914) list(APPEND mi_cflags /Zc:__cplusplus) endif() -if (${CMAKE_BUILD_TYPE} MATCHES Release OR ${CMAKE_BUILD_TYPE} MATCHES MinSizeRel) - if (MSVC) - list(APPEND mi_cflags /GL) - endif() -endif() - # extra needed libraries if(WIN32) list(APPEND mi_libraries psapi shell32 user32 advapi32 bcrypt) + set(pc_libraries "-lpsapi -lshell32 -luser32 -ladvapi32 -lbcrypt") else() - if(NOT ${CMAKE_C_COMPILER} MATCHES "android") - list(APPEND mi_libraries pthread) - find_library(LIBRT rt) - if(LIBRT) - list(APPEND mi_libraries ${LIBRT}) - endif() + set(pc_libraries "") + find_library(MI_LIBPTHREAD pthread) + if (MI_LIBPTHREAD) + list(APPEND mi_libraries ${MI_LIBPTHREAD}) + set(pc_libraries "${pc_libraries} -pthread") + endif() + find_library(MI_LIBRT rt) + if(MI_LIBRT) + list(APPEND mi_libraries ${MI_LIBRT}) + set(pc_libraries "${pc_libraries} -lrt") + endif() + find_library(MI_LIBATOMIC atomic) + if (NOT MI_LIBATOMIC AND MI_USE_LIBATOMIC) + set(MI_LIBATOMIC atomic) + endif() + if (MI_LIBATOMIC) + list(APPEND mi_libraries ${MI_LIBATOMIC}) + set(pc_libraries "${pc_libraries} -latomic") endif() endif() @@ -174,31 +327,200 @@ endif() # Install and output names # ----------------------------------------------------------------------------- +# dynamic/shared library and symlinks always go to /usr/local/lib equivalent +set(mi_install_libdir "${CMAKE_INSTALL_LIBDIR}") + +# static libraries and object files, includes, and cmake config files +# are either installed at top level, or use versioned directories for side-by-side installation (default) +if (MI_INSTALL_TOPLEVEL) + set(mi_install_objdir "${CMAKE_INSTALL_LIBDIR}") + set(mi_install_incdir "${CMAKE_INSTALL_INCLUDEDIR}") + set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc") +else() + set(mi_install_objdir "${CMAKE_INSTALL_LIBDIR}/mimalloc-${mi_version}") # for static library and object files + set(mi_install_incdir "${CMAKE_INSTALL_INCLUDEDIR}/mimalloc-${mi_version}") # for includes + set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc-${mi_version}") # for cmake package info +endif() + +set(mi_basename "mimalloc") +if(MI_SECURE) + set(mi_basename "${mi_basename}-secure") +endif() +if(MI_TRACK_VALGRIND) + set(mi_basename "${mi_basename}-valgrind") +endif() +if(MI_TRACK_ASAN) + set(mi_basename "${mi_basename}-asan") +endif() +string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC) +if(NOT(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel|none)$")) + set(mi_basename "${mi_basename}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version +endif() + +if(MI_BUILD_SHARED) + list(APPEND mi_build_targets "shared") +endif() +if(MI_BUILD_STATIC) + list(APPEND mi_build_targets "static") +endif() +if(MI_BUILD_OBJECT) + list(APPEND mi_build_targets "object") +endif() +if(MI_BUILD_TESTS) + list(APPEND mi_build_targets "tests") +endif() + message(STATUS "") +message(STATUS "Library base name: ${mi_basename}") +message(STATUS "Version : ${mi_version}") message(STATUS "Build type : ${CMAKE_BUILD_TYPE_LC}") if(MI_USE_CXX) - message(STATUS "Compiler : ${CMAKE_CXX_COMPILER}") + message(STATUS "C++ Compiler : ${CMAKE_CXX_COMPILER}") else() - message(STATUS "Compiler : ${CMAKE_C_COMPILER}") + message(STATUS "C Compiler : ${CMAKE_C_COMPILER}") endif() +message(STATUS "Compiler flags : ${mi_cflags}") +message(STATUS "Compiler defines : ${mi_defines}") +message(STATUS "Link libraries : ${mi_libraries}") +message(STATUS "Build targets : ${mi_build_targets}") message(STATUS "") -set(mimalloc_LIBRARIES ${mi_libraries}) -set(mimalloc_INTERFACE_LIBRARIES mimalloc) -set(mimalloc_INTERFACE_INCLUDE_DIRECTORIES ${mimalloc_FRAMEWORK_HEADERS_DIR}) -set(mimalloc_INTERFACE_DEPENDENCIES mimalloc_CopyHeaders) -set(mimalloc_PRIVATE_DEFINITIONS ${mi_defines} MI_STATIC_LIB) -set(mimalloc_COMPILE_OPTIONS ${mi_cflags}) -set(mimalloc_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include) - -WEBKIT_FRAMEWORK_DECLARE(mimalloc) -WEBKIT_INCLUDE_CONFIG_FILES_IF_EXISTS() - -WEBKIT_COPY_FILES(mimalloc_CopyHeaders - DESTINATION ${mimalloc_FRAMEWORK_HEADERS_DIR} - FILES ${mimalloc_PUBLIC_HEADERS} -) - -WEBKIT_WRAP_SOURCELIST(${mimalloc_SOURCES}) -WEBKIT_FRAMEWORK(mimalloc) -WEBKIT_FRAMEWORK_TARGET(mimalloc) +# ----------------------------------------------------------------------------- +# Main targets +# ----------------------------------------------------------------------------- + +# shared library +if(MI_BUILD_SHARED) + add_library(mimalloc SHARED ${mi_sources}) + set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} SOVERSION ${mi_version_major} OUTPUT_NAME ${mi_basename} ) + target_compile_definitions(mimalloc PRIVATE ${mi_defines} MI_SHARED_LIB MI_SHARED_LIB_EXPORT) + target_compile_options(mimalloc PRIVATE ${mi_cflags}) + target_link_libraries(mimalloc PRIVATE ${mi_libraries}) + target_include_directories(mimalloc PUBLIC + $ + $ + ) + if(WIN32 AND MI_WIN_REDIRECT) + # On windows, link and copy the mimalloc redirection dll too. + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(MIMALLOC_REDIRECT_SUFFIX "32") + else() + set(MIMALLOC_REDIRECT_SUFFIX "") + endif() + + target_link_libraries(mimalloc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.lib) + add_custom_command(TARGET mimalloc POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll" $ + COMMENT "Copy mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll to output directory") + install(FILES "$/mimalloc-redirect${MIMALLOC_REDIRECT_SUFFIX}.dll" DESTINATION ${mi_install_libdir}) + endif() + + install(TARGETS mimalloc EXPORT mimalloc DESTINATION ${mi_install_libdir} LIBRARY) + install(EXPORT mimalloc DESTINATION ${mi_install_cmakedir}) +endif() + +# static library +if (MI_BUILD_STATIC) + add_library(mimalloc-static STATIC ${mi_sources}) + set_property(TARGET mimalloc-static PROPERTY POSITION_INDEPENDENT_CODE ON) + target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB) + target_compile_options(mimalloc-static PRIVATE ${mi_cflags}) + target_link_libraries(mimalloc-static PRIVATE ${mi_libraries}) + target_include_directories(mimalloc-static PUBLIC + $ + $ + ) + if(WIN32) + # When building both static and shared libraries on Windows, a static library should use a + # different output name to avoid the conflict with the import library of a shared one. + string(REPLACE "mimalloc" "mimalloc-static" mi_output_name ${mi_basename}) + set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_output_name}) + else() + set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_basename}) + endif() + + install(TARGETS mimalloc-static EXPORT mimalloc DESTINATION ${mi_install_objdir} LIBRARY) + install(EXPORT mimalloc DESTINATION ${mi_install_cmakedir}) +endif() + +# install include files +install(FILES include/mimalloc.h DESTINATION ${mi_install_incdir}) +install(FILES include/mimalloc-override.h DESTINATION ${mi_install_incdir}) +install(FILES include/mimalloc-new-delete.h DESTINATION ${mi_install_incdir}) +install(FILES cmake/mimalloc-config.cmake DESTINATION ${mi_install_cmakedir}) +install(FILES cmake/mimalloc-config-version.cmake DESTINATION ${mi_install_cmakedir}) + + +# single object file for more predictable static overriding +if (MI_BUILD_OBJECT) + add_library(mimalloc-obj OBJECT src/static.c) + set_property(TARGET mimalloc-obj PROPERTY POSITION_INDEPENDENT_CODE ON) + target_compile_definitions(mimalloc-obj PRIVATE ${mi_defines}) + target_compile_options(mimalloc-obj PRIVATE ${mi_cflags}) + target_include_directories(mimalloc-obj PUBLIC + $ + $ + ) + + # Copy the generated object file (`static.o`) to the output directory (as `mimalloc.o`) + if(NOT WIN32) + set(mimalloc-obj-static "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/mimalloc-obj.dir/src/static.c${CMAKE_C_OUTPUT_EXTENSION}") + set(mimalloc-obj-out "${CMAKE_CURRENT_BINARY_DIR}/${mi_basename}${CMAKE_C_OUTPUT_EXTENSION}") + add_custom_command(OUTPUT ${mimalloc-obj-out} DEPENDS mimalloc-obj COMMAND "${CMAKE_COMMAND}" -E copy "${mimalloc-obj-static}" "${mimalloc-obj-out}") + add_custom_target(mimalloc-obj-target ALL DEPENDS ${mimalloc-obj-out}) + endif() + + # the following seems to lead to cmake warnings/errors on some systems, disable for now :-( + # install(TARGETS mimalloc-obj EXPORT mimalloc DESTINATION ${mi_install_objdir}) + + # the FILES expression can also be: $ + # but that fails cmake versions less than 3.10 so we leave it as is for now + install(FILES ${mimalloc-obj-static} + DESTINATION ${mi_install_objdir} + RENAME ${mi_basename}${CMAKE_C_OUTPUT_EXTENSION} ) +endif() + +# pkg-config file support +include("cmake/JoinPaths.cmake") +join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") +join_paths(libdir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_LIBDIR}") + +configure_file(mimalloc.pc.in mimalloc.pc @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mimalloc.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig/") + +# ----------------------------------------------------------------------------- +# API surface testing +# ----------------------------------------------------------------------------- + +if (MI_BUILD_TESTS) + enable_testing() + + foreach(TEST_NAME api api-fill stress) + add_executable(mimalloc-test-${TEST_NAME} test/test-${TEST_NAME}.c) + target_compile_definitions(mimalloc-test-${TEST_NAME} PRIVATE ${mi_defines}) + target_compile_options(mimalloc-test-${TEST_NAME} PRIVATE ${mi_cflags}) + target_include_directories(mimalloc-test-${TEST_NAME} PRIVATE include) + target_link_libraries(mimalloc-test-${TEST_NAME} PRIVATE mimalloc ${mi_libraries}) + + add_test(NAME test-${TEST_NAME} COMMAND mimalloc-test-${TEST_NAME}) + endforeach() +endif() + +# ----------------------------------------------------------------------------- +# Set override properties +# ----------------------------------------------------------------------------- +if (MI_OVERRIDE) + if (MI_BUILD_SHARED) + target_compile_definitions(mimalloc PRIVATE MI_MALLOC_OVERRIDE) + endif() + if(NOT WIN32) + # It is only possible to override malloc on Windows when building as a DLL. + if (MI_BUILD_STATIC) + target_compile_definitions(mimalloc-static PRIVATE MI_MALLOC_OVERRIDE) + endif() + if (MI_BUILD_OBJECT) + target_compile_definitions(mimalloc-obj PRIVATE MI_MALLOC_OVERRIDE) + endif() + endif() +endif() diff --git a/Source/mimalloc/azure-pipelines.yml b/Source/mimalloc/azure-pipelines.yml index aeb2908b5..0247c76fd 100644 --- a/Source/mimalloc/azure-pipelines.yml +++ b/Source/mimalloc/azure-pipelines.yml @@ -13,12 +13,12 @@ trigger: include: - v* -jobs: +jobs: - job: displayName: Windows pool: vmImage: - windows-2019 + windows-2022 strategy: matrix: Debug: @@ -43,7 +43,7 @@ jobs: solution: $(BuildType)/libmimalloc.sln configuration: '$(MSBuildConfiguration)' msbuildArguments: -m - - script: ctest --verbose --timeout 120 + - script: ctest --verbose --timeout 120 -C $(MSBuildConfiguration) workingDirectory: $(BuildType) displayName: CTest #- script: $(BuildType)\$(BuildType)\mimalloc-test-stress @@ -55,7 +55,7 @@ jobs: displayName: Linux pool: vmImage: - ubuntu-18.04 + ubuntu-22.04 strategy: matrix: Debug: @@ -98,6 +98,22 @@ jobs: CXX: clang++ BuildType: debug-clang-cxx cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_USE_CXX=ON + Debug ASAN Clang: + CC: clang + CXX: clang++ + BuildType: debug-asan-clang + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_TRACK_ASAN=ON + Debug UBSAN Clang: + CC: clang + CXX: clang++ + BuildType: debug-ubsan-clang + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON -DMI_DEBUG_UBSAN=ON + Debug TSAN Clang++: + CC: clang + CXX: clang++ + BuildType: debug-tsan-clang-cxx + cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_USE_CXX=ON -DMI_DEBUG_TSAN=ON + steps: - task: CMake@1 inputs: @@ -105,7 +121,7 @@ jobs: cmakeArgs: .. $(cmakeExtraArgs) - script: make -j$(nproc) -C $(BuildType) displayName: Make - - script: ctest --verbose --timeout 120 + - script: ctest --verbose --timeout 180 workingDirectory: $(BuildType) displayName: CTest # - upload: $(Build.SourcesDirectory)/$(BuildType) @@ -115,7 +131,7 @@ jobs: displayName: macOS pool: vmImage: - macOS-10.14 + macOS-latest strategy: matrix: Debug: @@ -134,9 +150,16 @@ jobs: cmakeArgs: .. $(cmakeExtraArgs) - script: make -j$(sysctl -n hw.ncpu) -C $(BuildType) displayName: Make + # - script: MIMALLOC_VERBOSE=1 ./mimalloc-test-api + # workingDirectory: $(BuildType) + # displayName: TestAPI + # - script: MIMALLOC_VERBOSE=1 ./mimalloc-test-stress + # workingDirectory: $(BuildType) + # displayName: TestStress - script: ctest --verbose --timeout 120 workingDirectory: $(BuildType) displayName: CTest + # - upload: $(Build.SourcesDirectory)/$(BuildType) # artifact: mimalloc-macos-$(BuildType) diff --git a/Source/mimalloc/bin/mimalloc-redirect.dll b/Source/mimalloc/bin/mimalloc-redirect.dll index b7bf1d099681fc73d15ee8dd1b86a48de0aca1d1..a3a3591ff9b07edada16ad83ca1391963a126b2d 100644 GIT binary patch delta 11663 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 YRN-`?yJ&yWfue&&)kQ;jCsr-{8zq?&y8r+H delta 6752 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 diff --git a/Source/mimalloc/bin/mimalloc-redirect32.lib b/Source/mimalloc/bin/mimalloc-redirect32.lib index 66173060fc557427e54f5b3ecae5ecbdbb20c591..87f19b8ec0f7ae1024ff508a738b517c71f11aad 100644 GIT binary patch delta 175 zcmew$_Caie84F7UkK4D&RxG<%6dC%eCu^~*PnKu3W9qBk9M5XV$Qe;K{r^!01_q|d z_AJtqx3Cq#l<5PNOpa%jnq0~r%ejdy_Sr+IDznLg9Q80&(v#P7aKKbv=E!9+ar}LK zvI}Pg%kc|hHzpqdG7QbE4otS;s$wb6w>ma?7gq&mb`jeFL!fCallOCpP3C82n;gz9 F2>|gZHwFLz delta 175 zcmew$_Caie84JsQAed~$vWrE4VOHj3Eq3+E@~n1Dvobfwvl=pTf@F^}FfcGpwr7!^ zyoId@rc58GWO6*K)Z|k3Sg@*xP*rA=1v%;yv&gcvCW0E0>U@|W7u$2 cK}2?ORe()11e(S&c|VufWPWD0$>H3R0ABk>+5i9m diff --git a/Source/mimalloc/bin/minject.exe b/Source/mimalloc/bin/minject.exe new file mode 100644 index 0000000000000000000000000000000000000000..625816f1f7774c1f57b70033ea4fbb5e7a14e270 GIT binary patch literal 20992 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 literal 0 HcmV?d00001 diff --git a/Source/mimalloc/bin/minject32.exe b/Source/mimalloc/bin/minject32.exe new file mode 100644 index 0000000000000000000000000000000000000000..6857ad0ce1ccd014ed02c99d2c13feb5783fddb9 GIT binary patch literal 18432 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 literal 0 HcmV?d00001 diff --git a/Source/mimalloc/cmake/JoinPaths.cmake b/Source/mimalloc/cmake/JoinPaths.cmake new file mode 100644 index 000000000..c68d91b84 --- /dev/null +++ b/Source/mimalloc/cmake/JoinPaths.cmake @@ -0,0 +1,23 @@ +# This module provides function for joining paths +# known from most languages +# +# SPDX-License-Identifier: (MIT OR CC0-1.0) +# Copyright 2020 Jan Tojnar +# https://github.com/jtojnar/cmake-snips +# +# Modelled after Python’s os.path.join +# https://docs.python.org/3.7/library/os.path.html#os.path.join +# Windows not supported +function(join_paths joined_path first_path_segment) + set(temp_path "${first_path_segment}") + foreach(current_segment IN LISTS ARGN) + if(NOT ("${current_segment}" STREQUAL "")) + if(IS_ABSOLUTE "${current_segment}") + set(temp_path "${current_segment}") + else() + set(temp_path "${temp_path}/${current_segment}") + endif() + endif() + endforeach() + set(${joined_path} "${temp_path}" PARENT_SCOPE) +endfunction() diff --git a/Source/mimalloc/cmake/mimalloc-config-version.cmake b/Source/mimalloc/cmake/mimalloc-config-version.cmake index ed95c19e9..855c44d22 100644 --- a/Source/mimalloc/cmake/mimalloc-config-version.cmake +++ b/Source/mimalloc/cmake/mimalloc-config-version.cmake @@ -1,5 +1,6 @@ -set(mi_version_major 1) -set(mi_version_minor 7) +set(mi_version_major 2) +set(mi_version_minor 1) +set(mi_version_patch 1) set(mi_version ${mi_version_major}.${mi_version_minor}) set(PACKAGE_VERSION ${mi_version}) diff --git a/Source/mimalloc/cmake/mimalloc-config.cmake b/Source/mimalloc/cmake/mimalloc-config.cmake index 024c97d95..a49b02a25 100644 --- a/Source/mimalloc/cmake/mimalloc-config.cmake +++ b/Source/mimalloc/cmake/mimalloc-config.cmake @@ -1,11 +1,14 @@ include(${CMAKE_CURRENT_LIST_DIR}/mimalloc.cmake) -get_filename_component(MIMALLOC_SHARE_DIR "${CMAKE_CURRENT_LIST_DIR}" PATH) # one up from the cmake dir, e.g. /usr/local/share/mimalloc-2.0 -if (MIMALLOC_SHARE_DIR MATCHES "/share/") - string(REPLACE "/share/" "/lib/" MIMALLOC_LIBRARY_DIR ${MIMALLOC_SHARE_DIR}) - string(REPLACE "/share/" "/include/" MIMALLOC_INCLUDE_DIR ${MIMALLOC_SHARE_DIR}) +get_filename_component(MIMALLOC_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}" PATH) # one up from the cmake dir, e.g. /usr/local/lib/cmake/mimalloc-2.0 +get_filename_component(MIMALLOC_VERSION_DIR "${CMAKE_CURRENT_LIST_DIR}" NAME) +string(REPLACE "/lib/cmake" "/lib" MIMALLOC_LIBRARY_DIR "${MIMALLOC_CMAKE_DIR}") +if("${MIMALLOC_VERSION_DIR}" EQUAL "mimalloc") + # top level install + string(REPLACE "/lib/cmake" "/include" MIMALLOC_INCLUDE_DIR "${MIMALLOC_CMAKE_DIR}") + set(MIMALLOC_OBJECT_DIR "${MIMALLOC_LIBRARY_DIR}") else() - # if MI_INSTALL_TOPLEVEL==ON - set(MIMALLOC_LIBRARY_DIR "${MIMALLOC_SHARE_DIR}/lib") - set(MIMALLOC_INCLUDE_DIR "${MIMALLOC_SHARE_DIR}/include") -endif() + # versioned + string(REPLACE "/lib/cmake/" "/include/" MIMALLOC_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}") + string(REPLACE "/lib/cmake/" "/lib/" MIMALLOC_OBJECT_DIR "${CMAKE_CURRENT_LIST_DIR}") +endif() set(MIMALLOC_TARGET_DIR "${MIMALLOC_LIBRARY_DIR}") # legacy diff --git a/Source/mimalloc/doc/doxyfile b/Source/mimalloc/doc/doxyfile index 6c1e30a0d..55cae8bf2 100644 --- a/Source/mimalloc/doc/doxyfile +++ b/Source/mimalloc/doc/doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.15 +# Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -38,7 +38,7 @@ PROJECT_NAME = mi-malloc # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 1.6 +PROJECT_NUMBER = 1.8/2.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -197,6 +197,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus @@ -217,6 +227,14 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. @@ -253,12 +271,6 @@ TAB_SIZE = 2 ALIASES = -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -299,19 +311,22 @@ OPTIMIZE_OUTPUT_SLICE = NO # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is -# Fortran), use: inc=Fortran f=C. +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = @@ -329,7 +344,7 @@ MARKDOWN_SUPPORT = YES # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 0. +# Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 @@ -445,6 +460,19 @@ TYPEDEF_HIDES_STRUCT = YES LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -465,6 +493,12 @@ EXTRACT_ALL = YES EXTRACT_PRIVATE = NO +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. @@ -502,6 +536,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -519,8 +560,8 @@ HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO @@ -539,11 +580,18 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. # The default value is: system dependent. CASE_SENSE_NAMES = NO @@ -782,7 +830,10 @@ WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO @@ -818,8 +869,8 @@ INPUT = mimalloc-doc.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 @@ -832,11 +883,15 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, -# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ @@ -1094,16 +1149,22 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO +# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories @@ -1113,10 +1174,13 @@ CLANG_ASSISTED_PARSING = NO CLANG_OPTIONS = # If clang assisted parsing is enabled you can provide the clang parser with the -# path to the compilation database (see: -# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files -# were built. This is equivalent to specifying the "-p" option to a clang tool, -# such as clang-check. These options will then be passed to the parser. +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. @@ -1133,13 +1197,6 @@ CLANG_DATABASE_PATH = ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored @@ -1278,9 +1335,9 @@ HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that -# are dynamically created via Javascript. If disabled, the navigation index will +# are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have Javascript, +# page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1310,10 +1367,11 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/xcode/), introduced with OSX -# 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. @@ -1355,8 +1413,8 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# (see: +# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1386,7 +1444,7 @@ CHM_FILE = HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the master .chm file (NO). +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1431,7 +1489,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1439,8 +1498,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1448,30 +1507,30 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1548,6 +1607,17 @@ TREEVIEW_WIDTH = 180 EXT_LINKS_IN_WINDOW = NO +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML @@ -1568,8 +1638,14 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1581,7 +1657,7 @@ USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. @@ -1597,7 +1673,7 @@ MATHJAX_FORMAT = HTML-CSS # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -1611,7 +1687,8 @@ MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1639,7 +1716,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1658,7 +1735,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1671,8 +1749,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1743,10 +1822,11 @@ LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex # The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to -# generate index for LaTeX. +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. # Note: This tag is used in the generated output file (.tex). # See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. -# The default value is: \makeindex. +# The default value is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_MAKEINDEX_CMD = \makeindex @@ -1835,9 +1915,11 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES, to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2076,6 +2158,10 @@ DOCBOOK_PROGRAMLISTING = NO GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to Sqlite3 output +#--------------------------------------------------------------------------- + #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- @@ -2238,12 +2324,6 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- @@ -2257,15 +2337,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2363,10 +2434,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2556,9 +2649,11 @@ DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc and +# plantuml temporary files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES diff --git a/Source/mimalloc/doc/ds-logo.jpg b/Source/mimalloc/doc/ds-logo.jpg index 853a727927532e3bf5c2ef17579907c195b6280d..c9abb1a91f16d1e0c9f1e0cb35b9110d97e10a64 100644 GIT binary patch literal 181497 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 l2>QT!tifjBrf8y%-XUzXt;2#V8 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 NulKFVM%}+h{vTq)O%4D6 diff --git a/Source/mimalloc/doc/ds-logo.png b/Source/mimalloc/doc/ds-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..93b84e446e7e510ace3c1e6177d1ab2c7274d46d GIT binary patch literal 121150 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�+VVCPTlGmsC>z-OtJwgh3r^o;RAOJ~3 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 struct mi_stl_allocator { } /*! \page build Building -Checkout the sources from Github: +Checkout the sources from GitHub: ``` git clone https://github.com/microsoft/mimalloc ``` @@ -1036,7 +1080,7 @@ or via environment variables. - `MIMALLOC_PAGE_RESET=0`: by default, mimalloc will reset (or purge) OS pages when not in use to signal to the OS that the underlying physical memory can be reused. This can reduce memory fragmentation in long running (server) programs. By setting it to `0` no such page resets will be done which can improve performance for programs that are not long - running. As an alternative, the `MIMALLOC_RESET_DELAY=` can be set higher (100ms by default) to make the page + running. As an alternative, the `MIMALLOC_DECOMMIT_DELAY=` can be set higher (100ms by default) to make the page reset occur less frequently instead of turning it off completely. - `MIMALLOC_LARGE_OS_PAGES=1`: use large OS pages (2MiB) when available; for some workloads this can significantly improve performance. Use `MIMALLOC_VERBOSE` to check if the large OS pages are enabled -- usually one needs @@ -1053,6 +1097,8 @@ or via environment variables. `MIMALLOC_EAGER_COMMIT_DELAY=N` (`N` is 1 by default) to delay the initial `N` segments (of 4MiB) of a thread to not allocate in the huge OS pages; this prevents threads that are short lived and allocate just a little to take up space in the huge OS page area (which cannot be reset). +- `MIMALLOC_RESERVE_HUGE_OS_PAGES_AT=N`: where N is the numa node. This reserves the huge pages at a specific numa node. + (`N` is -1 by default to reserve huge pages evenly among the given number of numa nodes (or use the available ones as detected)) Use caution when using `fork` in combination with either large or huge OS pages: on a fork, the OS uses copy-on-write for all pages in the original process including the huge OS pages. When any memory is now written in that area, the @@ -1158,6 +1204,12 @@ void* calloc(size_t size, size_t n); void* realloc(void* p, size_t newsize); void free(void* p); +void* aligned_alloc(size_t alignment, size_t size); +char* strdup(const char* s); +char* strndup(const char* s, size_t n); +char* realpath(const char* fname, char* resolved_name); + + // C++ void operator delete(void* p); void operator delete[](void* p); @@ -1177,16 +1229,24 @@ int posix_memalign(void** p, size_t alignment, size_t size); // Linux void* memalign(size_t alignment, size_t size); -void* aligned_alloc(size_t alignment, size_t size); void* valloc(size_t size); void* pvalloc(size_t size); size_t malloc_usable_size(void *p); +void* reallocf(void* p, size_t newsize); + +// macOS +void vfree(void* p); +size_t malloc_size(const void* p); +size_t malloc_good_size(size_t size); // BSD void* reallocarray( void* p, size_t count, size_t size ); void* reallocf(void* p, size_t newsize); void cfree(void* p); +// NetBSD +int reallocarr(void* p, size_t count, size_t size); + // Windows void* _expand(void* p, size_t newsize); size_t _msize(void* p); diff --git a/Source/mimalloc/doc/spades-logo.png b/Source/mimalloc/doc/spades-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d8c73fef436b101d2a89b70466a00ad18633bf50 GIT binary patch literal 34583 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 A-~a#s literal 0 HcmV?d00001 diff --git a/Source/mimalloc/doc/unreal-logo.svg b/Source/mimalloc/doc/unreal-logo.svg new file mode 100644 index 000000000..5d5192a25 --- /dev/null +++ b/Source/mimalloc/doc/unreal-logo.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + diff --git a/Source/mimalloc/ide/vs2017/mimalloc-override-test.vcxproj b/Source/mimalloc/ide/vs2017/mimalloc-override-test.vcxproj index faaa00e35..04c16a9fa 100644 --- a/Source/mimalloc/ide/vs2017/mimalloc-override-test.vcxproj +++ b/Source/mimalloc/ide/vs2017/mimalloc-override-test.vcxproj @@ -1,4 +1,4 @@ - + @@ -22,8 +22,8 @@ 15.0 {FEF7868F-750E-4C21-A04D-22707CC66879} mimalloc-override-test - 10.0.17134.0 mimalloc-override-test + 10.0.19041.0 diff --git a/Source/mimalloc/ide/vs2017/mimalloc-override.vcxproj b/Source/mimalloc/ide/vs2017/mimalloc-override.vcxproj index a1266dc99..123201bd4 100644 --- a/Source/mimalloc/ide/vs2017/mimalloc-override.vcxproj +++ b/Source/mimalloc/ide/vs2017/mimalloc-override.vcxproj @@ -22,8 +22,8 @@ 15.0 {ABB5EAE7-B3E6-432E-B636-333449892EA7} mimalloc-override - 10.0.17134.0 mimalloc-override + 10.0.19041.0 @@ -110,7 +110,7 @@ false - COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect32.dll $(OutputPath) + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" Copy mimalloc-redirect32.dll to the output directory @@ -138,7 +138,7 @@ false - COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect.dll $(OutputPath) + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" copy mimalloc-redirect.dll to the output directory @@ -170,7 +170,7 @@ false - COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect32.dll $(OutputPath) + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" Copy mimalloc-redirect32.dll to the output directory @@ -202,19 +202,21 @@ false - COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect.dll $(OutputPath) + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" copy mimalloc-redirect.dll to the output directory - - - + + + + + @@ -236,7 +238,7 @@ - + @@ -247,6 +249,7 @@ + diff --git a/Source/mimalloc/ide/vs2017/mimalloc-test-stress.vcxproj b/Source/mimalloc/ide/vs2017/mimalloc-test-stress.vcxproj index b8267d0b3..061b8605c 100644 --- a/Source/mimalloc/ide/vs2017/mimalloc-test-stress.vcxproj +++ b/Source/mimalloc/ide/vs2017/mimalloc-test-stress.vcxproj @@ -1,4 +1,4 @@ - + @@ -22,8 +22,8 @@ 15.0 {FEF7958F-750E-4C21-A04D-22707CC66878} mimalloc-test-stress - 10.0.17134.0 mimalloc-test-stress + 10.0.19041.0 diff --git a/Source/mimalloc/ide/vs2017/mimalloc-test.vcxproj b/Source/mimalloc/ide/vs2017/mimalloc-test.vcxproj index 27c7bb6ea..04bd6537b 100644 --- a/Source/mimalloc/ide/vs2017/mimalloc-test.vcxproj +++ b/Source/mimalloc/ide/vs2017/mimalloc-test.vcxproj @@ -1,4 +1,4 @@ - + @@ -22,8 +22,8 @@ 15.0 {FEF7858F-750E-4C21-A04D-22707CC66878} mimalloctest - 10.0.17134.0 mimalloc-test + 10.0.19041.0 @@ -102,7 +102,7 @@ true true ..\..\include - stdcpp17 + stdcpp14 Console diff --git a/Source/mimalloc/ide/vs2017/mimalloc.sln b/Source/mimalloc/ide/vs2017/mimalloc.sln index aeab6b88f..515c03f2e 100644 --- a/Source/mimalloc/ide/vs2017/mimalloc.sln +++ b/Source/mimalloc/ide/vs2017/mimalloc.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.28010.2016 +VisualStudioVersion = 15.0.26228.102 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc", "mimalloc.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA6}" EndProject diff --git a/Source/mimalloc/ide/vs2017/mimalloc.vcxproj b/Source/mimalloc/ide/vs2017/mimalloc.vcxproj index 8102b9fe7..a4ad5cbd2 100644 --- a/Source/mimalloc/ide/vs2017/mimalloc.vcxproj +++ b/Source/mimalloc/ide/vs2017/mimalloc.vcxproj @@ -22,7 +22,7 @@ 15.0 {ABB5EAE7-B3E6-432E-B636-333449892EA6} mimalloc - 10.0.17134.0 + 10.0.19041.0 mimalloc @@ -129,9 +129,9 @@ true ../../include _CRT_SECURE_NO_WARNINGS;MI_DEBUG=3;%(PreprocessorDefinitions); - CompileAsC + CompileAsCpp false - stdcpp17 + stdcpp14 @@ -215,12 +215,6 @@ false false - - true - true - true - true - true true @@ -233,7 +227,7 @@ - + true @@ -243,17 +237,20 @@ + - - - + + + + + diff --git a/Source/mimalloc/ide/vs2019/mimalloc-override.vcxproj b/Source/mimalloc/ide/vs2019/mimalloc-override.vcxproj index 182ddab19..f219fa04a 100644 --- a/Source/mimalloc/ide/vs2019/mimalloc-override.vcxproj +++ b/Source/mimalloc/ide/vs2019/mimalloc-override.vcxproj @@ -110,7 +110,7 @@ false - COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect32.dll $(OutputPath) + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" Copy mimalloc-redirect32.dll to the output directory @@ -138,7 +138,7 @@ false - COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect.dll $(OutputPath) + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" copy mimalloc-redirect.dll to the output directory @@ -170,7 +170,7 @@ false - COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect32.dll $(OutputPath) + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" Copy mimalloc-redirect32.dll to the output directory @@ -202,19 +202,21 @@ false - COPY /Y $(ProjectDir)..\..\bin\mimalloc-redirect.dll $(OutputPath) + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" copy mimalloc-redirect.dll to the output directory - - - + + + + + @@ -236,7 +238,7 @@ - + @@ -247,6 +249,7 @@ + diff --git a/Source/mimalloc/ide/vs2019/mimalloc.sln b/Source/mimalloc/ide/vs2019/mimalloc.sln index fcb938a4f..6ff01d3b4 100644 --- a/Source/mimalloc/ide/vs2019/mimalloc.sln +++ b/Source/mimalloc/ide/vs2019/mimalloc.sln @@ -1,81 +1,81 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29709.97 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc", "mimalloc.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test", "mimalloc-test.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66878}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override", "mimalloc-override.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA7}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-test", "mimalloc-override-test.vcxproj", "{FEF7868F-750E-4C21-A04D-22707CC66879}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-stress", "mimalloc-test-stress.vcxproj", "{FEF7958F-750E-4C21-A04D-22707CC66878}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-api", "mimalloc-test-api.vcxproj", "{FFF7958F-750E-4C21-A04D-22707CC66878}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.ActiveCfg = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.Build.0 = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.ActiveCfg = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.Build.0 = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.ActiveCfg = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.Build.0 = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.ActiveCfg = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.Build.0 = Release|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 - {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.ActiveCfg = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.Build.0 = Debug|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.ActiveCfg = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.Build.0 = Debug|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.ActiveCfg = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.Build.0 = Release|x64 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.ActiveCfg = Release|Win32 - {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.Build.0 = Release|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32 - {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 - {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 - {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {4297F93D-486A-4243-995F-7D32F59AE82A} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29709.97 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc", "mimalloc.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test", "mimalloc-test.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66878}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override", "mimalloc-override.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-test", "mimalloc-override-test.vcxproj", "{FEF7868F-750E-4C21-A04D-22707CC66879}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-stress", "mimalloc-test-stress.vcxproj", "{FEF7958F-750E-4C21-A04D-22707CC66878}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-api", "mimalloc-test-api.vcxproj", "{FFF7958F-750E-4C21-A04D-22707CC66878}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.ActiveCfg = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.Build.0 = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.ActiveCfg = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.Build.0 = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.ActiveCfg = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.Build.0 = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.ActiveCfg = Release|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.Build.0 = Release|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.ActiveCfg = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.Build.0 = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.ActiveCfg = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.Build.0 = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.ActiveCfg = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.Build.0 = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.ActiveCfg = Release|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.Build.0 = Release|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4297F93D-486A-4243-995F-7D32F59AE82A} + EndGlobalSection +EndGlobal diff --git a/Source/mimalloc/ide/vs2019/mimalloc.vcxproj b/Source/mimalloc/ide/vs2019/mimalloc.vcxproj index 6c7e276c7..115a5058a 100644 --- a/Source/mimalloc/ide/vs2019/mimalloc.vcxproj +++ b/Source/mimalloc/ide/vs2019/mimalloc.vcxproj @@ -92,7 +92,7 @@ - Level3 + Level4 Disabled true true @@ -114,7 +114,7 @@ Level4 Disabled true - true + Default ../../include MI_DEBUG=3;%(PreprocessorDefinitions); CompileAsCpp @@ -138,7 +138,7 @@ - Level3 + Level4 MaxSpeed true true @@ -166,7 +166,7 @@ - Level3 + Level4 MaxSpeed true true @@ -205,12 +205,6 @@ false false - - true - true - true - true - true true @@ -225,7 +219,13 @@ - + + + true + true + true + true + true @@ -235,17 +235,20 @@ + - - - + + + + + diff --git a/Source/mimalloc/ide/vs2022/mimalloc-override-test.vcxproj b/Source/mimalloc/ide/vs2022/mimalloc-override-test.vcxproj new file mode 100644 index 000000000..a3c56f7ba --- /dev/null +++ b/Source/mimalloc/ide/vs2022/mimalloc-override-test.vcxproj @@ -0,0 +1,190 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {FEF7868F-750E-4C21-A04D-22707CC66879} + mimalloc-override-test + 10.0 + mimalloc-override-test + + + + Application + true + v143 + + + Application + false + v143 + true + + + Application + true + v143 + + + Application + false + v143 + true + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + kernel32.lib;%(AdditionalDependencies) + + + + + + + + + + Level3 + Disabled + true + true + ..\..\include + MultiThreadedDebugDLL + Sync + Default + false + + + Console + + + kernel32.lib;%(AdditionalDependencies) + + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + + + true + true + Console + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + MultiThreadedDLL + + + true + true + Console + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + {abb5eae7-b3e6-432e-b636-333449892ea7} + + + + + + \ No newline at end of file diff --git a/Source/mimalloc/ide/vs2022/mimalloc-override.vcxproj b/Source/mimalloc/ide/vs2022/mimalloc-override.vcxproj new file mode 100644 index 000000000..c58648eec --- /dev/null +++ b/Source/mimalloc/ide/vs2022/mimalloc-override.vcxproj @@ -0,0 +1,270 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {ABB5EAE7-B3E6-432E-B636-333449892EA7} + mimalloc-override + 10.0 + mimalloc-override + + + + DynamicLibrary + true + v143 + + + DynamicLibrary + false + v143 + + + DynamicLibrary + true + v143 + + + DynamicLibrary + false + v143 + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc-override + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc-override + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc-override + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .dll + mimalloc-override + + + + Level3 + Disabled + true + true + ../../include + MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); + MultiThreadedDebugDLL + false + Default + + + $(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies) + + + + + Default + false + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" + + + Copy mimalloc-redirect32.dll to the output directory + + + + + Level3 + Disabled + true + true + ../../include + MI_DEBUG=4;MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions); + MultiThreadedDebugDLL + false + Default + + + $(ProjectDir)\..\..\bin\mimalloc-redirect.lib;%(AdditionalDependencies) + + + + + Default + false + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" + + + copy mimalloc-redirect.dll to the output directory + + + + + Level3 + MaxSpeed + true + true + true + ../../include + MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + MultiThreadedDLL + Default + false + + + true + true + $(ProjectDir)\..\..\bin\mimalloc-redirect32.lib;%(AdditionalDependencies) + + + Default + false + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect32.dll" "$(OutputPath)" + + + Copy mimalloc-redirect32.dll to the output directory + + + + + Level3 + MaxSpeed + true + true + true + ../../include + MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + MultiThreadedDLL + Default + false + + + true + true + $(ProjectDir)\..\..\bin\mimalloc-redirect.lib;%(AdditionalDependencies) + + + Default + false + + + COPY /Y "$(ProjectDir)..\..\bin\mimalloc-redirect.dll" "$(OutputPath)" + + + copy mimalloc-redirect.dll to the output directory + + + + + + + + + + + + + + + + + + false + false + false + false + + + true + true + true + true + + + + + + + + + + true + true + true + true + + + + + true + true + true + true + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/mimalloc/ide/vs2022/mimalloc-test-api.vcxproj b/Source/mimalloc/ide/vs2022/mimalloc-test-api.vcxproj new file mode 100644 index 000000000..6023c251f --- /dev/null +++ b/Source/mimalloc/ide/vs2022/mimalloc-test-api.vcxproj @@ -0,0 +1,155 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {FFF7958F-750E-4C21-A04D-22707CC66878} + mimalloc-test-api + 10.0 + mimalloc-test-api + + + + Application + true + v143 + + + Application + false + v143 + true + + + Application + true + v143 + + + Application + false + v143 + true + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + true + true + ..\..\include + + + Console + + + + + Level3 + Disabled + true + true + ..\..\include + + + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + %(PreprocessorDefinitions);NDEBUG + + + true + true + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + %(PreprocessorDefinitions);NDEBUG + + + true + true + Console + + + + + + + + + {abb5eae7-b3e6-432e-b636-333449892ea6} + + + + + + \ No newline at end of file diff --git a/Source/mimalloc/ide/vs2022/mimalloc-test-stress.vcxproj b/Source/mimalloc/ide/vs2022/mimalloc-test-stress.vcxproj new file mode 100644 index 000000000..14bd3e692 --- /dev/null +++ b/Source/mimalloc/ide/vs2022/mimalloc-test-stress.vcxproj @@ -0,0 +1,159 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {FEF7958F-750E-4C21-A04D-22707CC66878} + mimalloc-test-stress + 10.0 + mimalloc-test-stress + + + + Application + true + v143 + + + Application + false + v143 + true + + + Application + true + v143 + + + Application + false + v143 + true + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + true + true + ..\..\include + + + Console + + + + + Level3 + Disabled + true + true + ..\..\include + + + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + %(PreprocessorDefinitions);NDEBUG + + + true + true + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + %(PreprocessorDefinitions);NDEBUG + + + true + true + Console + + + + + false + false + false + false + + + + + {abb5eae7-b3e6-432e-b636-333449892ea7} + + + + + + \ No newline at end of file diff --git a/Source/mimalloc/ide/vs2022/mimalloc-test.vcxproj b/Source/mimalloc/ide/vs2022/mimalloc-test.vcxproj new file mode 100644 index 000000000..506dd7d45 --- /dev/null +++ b/Source/mimalloc/ide/vs2022/mimalloc-test.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {FEF7858F-750E-4C21-A04D-22707CC66878} + mimalloctest + 10.0 + mimalloc-test + + + + Application + true + v143 + + + Application + false + v143 + true + + + Application + true + v143 + + + Application + false + v143 + true + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + $(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + true + true + ..\..\include + stdcpp17 + + + Console + + + + + Level3 + Disabled + true + true + ..\..\include + stdcpp17 + + + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + stdcpp17 + + + true + true + Console + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\include + _MBCS;%(PreprocessorDefinitions);NDEBUG + stdcpp17 + + + true + true + Console + + + + + {abb5eae7-b3e6-432e-b636-333449892ea6} + + + + + + + + + \ No newline at end of file diff --git a/Source/mimalloc/ide/vs2022/mimalloc.sln b/Source/mimalloc/ide/vs2022/mimalloc.sln new file mode 100644 index 000000000..6ff01d3b4 --- /dev/null +++ b/Source/mimalloc/ide/vs2022/mimalloc.sln @@ -0,0 +1,81 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29709.97 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc", "mimalloc.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test", "mimalloc-test.vcxproj", "{FEF7858F-750E-4C21-A04D-22707CC66878}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override", "mimalloc-override.vcxproj", "{ABB5EAE7-B3E6-432E-B636-333449892EA7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-override-test", "mimalloc-override-test.vcxproj", "{FEF7868F-750E-4C21-A04D-22707CC66879}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-stress", "mimalloc-test-stress.vcxproj", "{FEF7958F-750E-4C21-A04D-22707CC66878}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mimalloc-test-api", "mimalloc-test-api.vcxproj", "{FFF7958F-750E-4C21-A04D-22707CC66878}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.ActiveCfg = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x64.Build.0 = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.ActiveCfg = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Debug|x86.Build.0 = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.ActiveCfg = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x64.Build.0 = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.ActiveCfg = Release|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA6}.Release|x86.Build.0 = Release|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 + {FEF7858F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.ActiveCfg = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x64.Build.0 = Debug|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.ActiveCfg = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Debug|x86.Build.0 = Debug|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.ActiveCfg = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x64.Build.0 = Release|x64 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.ActiveCfg = Release|Win32 + {ABB5EAE7-B3E6-432E-B636-333449892EA7}.Release|x86.Build.0 = Release|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.ActiveCfg = Debug|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x64.Build.0 = Debug|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Debug|x86.Build.0 = Debug|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.ActiveCfg = Release|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x64.Build.0 = Release|x64 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.ActiveCfg = Release|Win32 + {FEF7868F-750E-4C21-A04D-22707CC66879}.Release|x86.Build.0 = Release|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 + {FEF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.ActiveCfg = Debug|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x64.Build.0 = Debug|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.ActiveCfg = Debug|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Debug|x86.Build.0 = Debug|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.ActiveCfg = Release|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x64.Build.0 = Release|x64 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.ActiveCfg = Release|Win32 + {FFF7958F-750E-4C21-A04D-22707CC66878}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4297F93D-486A-4243-995F-7D32F59AE82A} + EndGlobalSection +EndGlobal diff --git a/Source/mimalloc/ide/vs2022/mimalloc.vcxproj b/Source/mimalloc/ide/vs2022/mimalloc.vcxproj new file mode 100644 index 000000000..07a854abe --- /dev/null +++ b/Source/mimalloc/ide/vs2022/mimalloc.vcxproj @@ -0,0 +1,258 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {ABB5EAE7-B3E6-432E-B636-333449892EA6} + mimalloc + 10.0 + mimalloc + + + + StaticLibrary + true + v143 + + + StaticLibrary + false + v143 + true + + + StaticLibrary + true + v143 + + + StaticLibrary + false + v143 + true + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + $(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\ + $(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\ + .lib + mimalloc-static + + + + Level4 + Disabled + true + Default + ../../include + MI_DEBUG=3;%(PreprocessorDefinitions); + CompileAsCpp + false + stdcpp20 + + + + + + + + + + + Level4 + Disabled + true + Default + ../../include + MI_DEBUG=4;MI_SECURE=0;%(PreprocessorDefinitions); + CompileAsCpp + false + stdcpp20 + + + + + + + + + + + + + + + + + + + Level4 + MaxSpeed + true + Default + ../../include + %(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + false + Default + CompileAsCpp + true + stdcpp20 + + + true + true + + + + + + + + + + + Level4 + MaxSpeed + true + Default + ../../include + %(PreprocessorDefinitions);NDEBUG + AssemblyAndSourceCode + $(IntDir) + false + false + Default + CompileAsCpp + true + stdcpp20 + + + true + true + + + + + + + + + + + + + + + + + false + false + false + false + + + true + true + true + true + + + + + + false + + + + + + true + true + true + true + + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/mimalloc/include/mimalloc-new-delete.h b/Source/mimalloc/include/mimalloc-new-delete.h index ba208f055..c16f4a665 100644 --- a/Source/mimalloc/include/mimalloc-new-delete.h +++ b/Source/mimalloc/include/mimalloc-new-delete.h @@ -22,14 +22,26 @@ terms of the MIT license. A copy of the license can be found in the file #include #include + #if defined(_MSC_VER) && defined(_Ret_notnull_) && defined(_Post_writable_byte_size_) + // stay consistent with VCRT definitions + #define mi_decl_new(n) mi_decl_nodiscard mi_decl_restrict _Ret_notnull_ _Post_writable_byte_size_(n) + #define mi_decl_new_nothrow(n) mi_decl_nodiscard mi_decl_restrict _Ret_maybenull_ _Success_(return != NULL) _Post_writable_byte_size_(n) + #else + #define mi_decl_new(n) mi_decl_nodiscard mi_decl_restrict + #define mi_decl_new_nothrow(n) mi_decl_nodiscard mi_decl_restrict + #endif + void operator delete(void* p) noexcept { mi_free(p); }; void operator delete[](void* p) noexcept { mi_free(p); }; - void* operator new(std::size_t n) noexcept(false) { return mi_new(n); } - void* operator new[](std::size_t n) noexcept(false) { return mi_new(n); } + void operator delete (void* p, const std::nothrow_t&) noexcept { mi_free(p); } + void operator delete[](void* p, const std::nothrow_t&) noexcept { mi_free(p); } + + mi_decl_new(n) void* operator new(std::size_t n) noexcept(false) { return mi_new(n); } + mi_decl_new(n) void* operator new[](std::size_t n) noexcept(false) { return mi_new(n); } - void* operator new (std::size_t n, const std::nothrow_t& tag) noexcept { (void)(tag); return mi_new_nothrow(n); } - void* operator new[](std::size_t n, const std::nothrow_t& tag) noexcept { (void)(tag); return mi_new_nothrow(n); } + mi_decl_new_nothrow(n) void* operator new (std::size_t n, const std::nothrow_t& tag) noexcept { (void)(tag); return mi_new_nothrow(n); } + mi_decl_new_nothrow(n) void* operator new[](std::size_t n, const std::nothrow_t& tag) noexcept { (void)(tag); return mi_new_nothrow(n); } #if (__cplusplus >= 201402L || _MSC_VER >= 1916) void operator delete (void* p, std::size_t n) noexcept { mi_free_size(p,n); }; @@ -41,9 +53,11 @@ terms of the MIT license. A copy of the license can be found in the file void operator delete[](void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast(al)); } void operator delete (void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast(al)); }; void operator delete[](void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast(al)); }; + void operator delete (void* p, std::align_val_t al, const std::nothrow_t&) noexcept { mi_free_aligned(p, static_cast(al)); } + void operator delete[](void* p, std::align_val_t al, const std::nothrow_t&) noexcept { mi_free_aligned(p, static_cast(al)); } - void* operator new( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast(al)); } - void* operator new[]( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast(al)); } + void* operator new (std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast(al)); } + void* operator new[](std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast(al)); } void* operator new (std::size_t n, std::align_val_t al, const std::nothrow_t&) noexcept { return mi_new_aligned_nothrow(n, static_cast(al)); } void* operator new[](std::size_t n, std::align_val_t al, const std::nothrow_t&) noexcept { return mi_new_aligned_nothrow(n, static_cast(al)); } #endif diff --git a/Source/mimalloc/include/mimalloc-override.h b/Source/mimalloc/include/mimalloc-override.h index 7d9f3e7d0..c63b0b91a 100644 --- a/Source/mimalloc/include/mimalloc-override.h +++ b/Source/mimalloc/include/mimalloc-override.h @@ -48,6 +48,7 @@ not accidentally mix pointers from different allocators). #define valloc(n) mi_valloc(n) #define pvalloc(n) mi_pvalloc(n) #define reallocarray(p,s,n) mi_reallocarray(p,s,n) +#define reallocarr(p,s,n) mi_reallocarr(p,s,n) #define memalign(a,n) mi_memalign(a,n) #define aligned_alloc(a,n) mi_aligned_alloc(a,n) #define posix_memalign(p,a,n) mi_posix_memalign(p,a,n) diff --git a/Source/mimalloc/include/mimalloc.h b/Source/mimalloc/include/mimalloc.h index fe5aa8f34..800cfd7e4 100644 --- a/Source/mimalloc/include/mimalloc.h +++ b/Source/mimalloc/include/mimalloc.h @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2023, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file #ifndef MIMALLOC_H #define MIMALLOC_H -#define MI_MALLOC_VERSION 171 // major + 2 digits minor +#define MI_MALLOC_VERSION 211 // major + 2 digits minor // ------------------------------------------------------ // Compiler specific attributes @@ -26,8 +26,10 @@ terms of the MIT license. A copy of the license can be found in the file #if defined(__cplusplus) && (__cplusplus >= 201703) #define mi_decl_nodiscard [[nodiscard]] -#elif (__GNUC__ >= 4) || defined(__clang__) // includes clang, icc, and clang-cl +#elif (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) // includes clang, icc, and clang-cl #define mi_decl_nodiscard __attribute__((warn_unused_result)) +#elif defined(_HAS_NODISCARD) + #define mi_decl_nodiscard _NODISCARD #elif (_MSC_VER >= 1700) #define mi_decl_nodiscard _Check_return_ #else @@ -58,8 +60,12 @@ terms of the MIT license. A copy of the license can be found in the file #define mi_attr_alloc_size2(s1,s2) #define mi_attr_alloc_align(p) #elif defined(__GNUC__) // includes clang and icc + #if defined(MI_SHARED_LIB) && defined(MI_SHARED_LIB_EXPORT) + #define mi_decl_export __attribute__((visibility("default"))) + #else + #define mi_decl_export + #endif #define mi_cdecl // leads to warnings... __attribute__((cdecl)) - #define mi_decl_export __attribute__((visibility("default"))) #define mi_decl_restrict #define mi_attr_malloc __attribute__((malloc)) #if (defined(__clang_major__) && (__clang_major__ < 4)) || (__GNUC__ < 5) @@ -91,6 +97,7 @@ terms of the MIT license. A copy of the license can be found in the file #include // size_t #include // bool +#include // INTPTR_MAX #ifdef __cplusplus extern "C" { @@ -153,8 +160,8 @@ mi_decl_export void mi_thread_init(void) mi_attr_noexcept; mi_decl_export void mi_thread_done(void) mi_attr_noexcept; mi_decl_export void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept; -mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, size_t* system_msecs, - size_t* current_rss, size_t* peak_rss, +mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, size_t* system_msecs, + size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults) mi_attr_noexcept; // ------------------------------------------------------------------------------------- @@ -249,8 +256,9 @@ typedef struct mi_heap_area_s { void* blocks; // start of the area containing heap blocks size_t reserved; // bytes reserved for this area (virtual) size_t committed; // current available bytes for this area - size_t used; // bytes in use by allocated blocks + size_t used; // number of allocated blocks size_t block_size; // size in bytes of each block + size_t full_block_size; // size in bytes of a full block including padding and metadata. } mi_heap_area_t; typedef bool (mi_cdecl mi_block_visit_fun)(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg); @@ -267,6 +275,19 @@ mi_decl_export int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size mi_decl_export int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noexcept; mi_decl_export bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) mi_attr_noexcept; +mi_decl_export void mi_debug_show_arenas(void) mi_attr_noexcept; + +// Experimental: heaps associated with specific memory arena's +typedef int mi_arena_id_t; +mi_decl_export void* mi_arena_area(mi_arena_id_t arena_id, size_t* size); +mi_decl_export int mi_reserve_huge_os_pages_at_ex(size_t pages, int numa_node, size_t timeout_msecs, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; +mi_decl_export int mi_reserve_os_memory_ex(size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; +mi_decl_export bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; + +#if MI_MALLOC_VERSION >= 200 +// Create a heap that only allocates in the specified arena +mi_decl_nodiscard mi_decl_export mi_heap_t* mi_heap_new_in_arena(mi_arena_id_t arena_id); +#endif // deprecated mi_decl_export int mi_reserve_huge_os_pages(size_t pages, double max_secs, size_t* pages_reserved) mi_attr_noexcept; @@ -292,7 +313,7 @@ mi_decl_export int mi_reserve_huge_os_pages(size_t pages, double max_secs, size // ------------------------------------------------------ -// Options, all `false` by default +// Options // ------------------------------------------------------ typedef enum mi_option_e { @@ -300,24 +321,31 @@ typedef enum mi_option_e { mi_option_show_errors, mi_option_show_stats, mi_option_verbose, - // the following options are experimental + // some of the following options are experimental + // (deprecated options are kept for binary backward compatibility with v1.x versions) mi_option_eager_commit, - mi_option_eager_region_commit, - mi_option_reset_decommits, - mi_option_large_os_pages, // implies eager commit - mi_option_reserve_huge_os_pages, - mi_option_reserve_os_memory, - mi_option_segment_cache, + mi_option_deprecated_eager_region_commit, + mi_option_deprecated_reset_decommits, + mi_option_large_os_pages, // use large (2MiB) OS pages, implies eager commit + mi_option_reserve_huge_os_pages, // reserve N huge OS pages (1GiB) at startup + mi_option_reserve_huge_os_pages_at, // reserve huge OS pages at a specific NUMA node + mi_option_reserve_os_memory, // reserve specified amount of OS memory at startup + mi_option_deprecated_segment_cache, mi_option_page_reset, - mi_option_abandoned_page_reset, - mi_option_segment_reset, + mi_option_abandoned_page_decommit, + mi_option_deprecated_segment_reset, mi_option_eager_commit_delay, - mi_option_reset_delay, - mi_option_use_numa_nodes, - mi_option_limit_os_alloc, + mi_option_decommit_delay, + mi_option_use_numa_nodes, // 0 = use available numa nodes, otherwise use at most N nodes. + mi_option_limit_os_alloc, // 1 = do not use OS memory for allocation (but only reserved arenas) mi_option_os_tag, mi_option_max_errors, mi_option_max_warnings, + mi_option_max_segment_reclaim, + mi_option_allow_decommit, + mi_option_segment_decommit_delay, + mi_option_decommit_extend_delay, + mi_option_destroy_on_exit, _mi_option_last } mi_option_t; @@ -329,6 +357,7 @@ mi_decl_export void mi_option_set_enabled(mi_option_t option, bool enable); mi_decl_export void mi_option_set_enabled_default(mi_option_t option, bool enable); mi_decl_nodiscard mi_decl_export long mi_option_get(mi_option_t option); +mi_decl_nodiscard mi_decl_export long mi_option_get_clamp(mi_option_t option, long min, long max); mi_decl_export void mi_option_set(mi_option_t option, long value); mi_decl_export void mi_option_set_default(mi_option_t option, long value); @@ -342,6 +371,7 @@ mi_decl_export void mi_option_set_default(mi_option_t option, long value); mi_decl_export void mi_cfree(void* p) mi_attr_noexcept; mi_decl_export void* mi__expand(void* p, size_t newsize) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export size_t mi_malloc_size(const void* p) mi_attr_noexcept; +mi_decl_nodiscard mi_decl_export size_t mi_malloc_good_size(size_t size) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept; mi_decl_export int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept; @@ -351,6 +381,7 @@ mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_pvalloc(size_t size) mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_aligned_alloc(size_t alignment, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2) mi_attr_alloc_align(1); mi_decl_nodiscard mi_decl_export void* mi_reallocarray(void* p, size_t count, size_t size) mi_attr_noexcept mi_attr_alloc_size2(2,3); +mi_decl_nodiscard mi_decl_export int mi_reallocarr(void* p, size_t count, size_t size) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export void* mi_aligned_recalloc(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export void* mi_aligned_offset_recalloc(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept; @@ -373,6 +404,9 @@ mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_new_n(size_t count, s mi_decl_nodiscard mi_decl_export void* mi_new_realloc(void* p, size_t newsize) mi_attr_alloc_size(2); mi_decl_nodiscard mi_decl_export void* mi_new_reallocn(void* p, size_t newcount, size_t size) mi_attr_alloc_size2(2, 3); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_alloc_new(mi_heap_t* heap, size_t size) mi_attr_malloc mi_attr_alloc_size(2); +mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_heap_alloc_new_n(mi_heap_t* heap, size_t count, size_t size) mi_attr_malloc mi_attr_alloc_size2(2, 3); + #ifdef __cplusplus } #endif @@ -383,13 +417,14 @@ mi_decl_nodiscard mi_decl_export void* mi_new_reallocn(void* p, size_t newcount, // --------------------------------------------------------------------------------------------- #ifdef __cplusplus +#include // std::size_t #include // PTRDIFF_MAX #if (__cplusplus >= 201103L) || (_MSC_VER > 1900) // C++11 #include // std::true_type #include // std::forward #endif -template struct mi_stl_allocator { +template struct _mi_stl_allocator_common { typedef T value_type; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; @@ -397,6 +432,27 @@ template struct mi_stl_allocator { typedef value_type const& const_reference; typedef value_type* pointer; typedef value_type const* const_pointer; + + #if ((__cplusplus >= 201103L) || (_MSC_VER > 1900)) // C++11 + using propagate_on_container_copy_assignment = std::true_type; + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; + template void construct(U* p, Args&& ...args) { ::new(p) U(std::forward(args)...); } + template void destroy(U* p) mi_attr_noexcept { p->~U(); } + #else + void construct(pointer p, value_type const& val) { ::new(p) value_type(val); } + void destroy(pointer p) { p->~value_type(); } + #endif + + size_type max_size() const mi_attr_noexcept { return (PTRDIFF_MAX/sizeof(value_type)); } + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } +}; + +template struct mi_stl_allocator : public _mi_stl_allocator_common { + using typename _mi_stl_allocator_common::size_type; + using typename _mi_stl_allocator_common::value_type; + using typename _mi_stl_allocator_common::pointer; template struct rebind { typedef mi_stl_allocator other; }; mi_stl_allocator() mi_attr_noexcept = default; @@ -413,24 +469,91 @@ template struct mi_stl_allocator { #endif #if ((__cplusplus >= 201103L) || (_MSC_VER > 1900)) // C++11 - using propagate_on_container_copy_assignment = std::true_type; - using propagate_on_container_move_assignment = std::true_type; - using propagate_on_container_swap = std::true_type; - using is_always_equal = std::true_type; - template void construct(U* p, Args&& ...args) { ::new(p) U(std::forward(args)...); } - template void destroy(U* p) mi_attr_noexcept { p->~U(); } - #else - void construct(pointer p, value_type const& val) { ::new(p) value_type(val); } - void destroy(pointer p) { p->~value_type(); } + using is_always_equal = std::true_type; #endif - - size_type max_size() const mi_attr_noexcept { return (PTRDIFF_MAX/sizeof(value_type)); } - pointer address(reference x) const { return &x; } - const_pointer address(const_reference x) const { return &x; } }; template bool operator==(const mi_stl_allocator& , const mi_stl_allocator& ) mi_attr_noexcept { return true; } template bool operator!=(const mi_stl_allocator& , const mi_stl_allocator& ) mi_attr_noexcept { return false; } + + +#if (__cplusplus >= 201103L) || (_MSC_VER >= 1900) // C++11 +#define MI_HAS_HEAP_STL_ALLOCATOR 1 + +#include // std::shared_ptr + +// Common base class for STL allocators in a specific heap +template struct _mi_heap_stl_allocator_common : public _mi_stl_allocator_common { + using typename _mi_stl_allocator_common::size_type; + using typename _mi_stl_allocator_common::value_type; + using typename _mi_stl_allocator_common::pointer; + + _mi_heap_stl_allocator_common(mi_heap_t* hp) : heap(hp) { } /* will not delete nor destroy the passed in heap */ + + #if (__cplusplus >= 201703L) // C++17 + mi_decl_nodiscard T* allocate(size_type count) { return static_cast(mi_heap_alloc_new_n(this->heap.get(), count, sizeof(T))); } + mi_decl_nodiscard T* allocate(size_type count, const void*) { return allocate(count); } + #else + mi_decl_nodiscard pointer allocate(size_type count, const void* = 0) { return static_cast(mi_heap_alloc_new_n(this->heap.get(), count, sizeof(value_type))); } + #endif + + #if ((__cplusplus >= 201103L) || (_MSC_VER > 1900)) // C++11 + using is_always_equal = std::false_type; + #endif + + void collect(bool force) { mi_heap_collect(this->heap.get(), force); } + template bool is_equal(const _mi_heap_stl_allocator_common& x) const { return (this->heap == x.heap); } + +protected: + std::shared_ptr heap; + template friend struct _mi_heap_stl_allocator_common; + + _mi_heap_stl_allocator_common() { + mi_heap_t* hp = mi_heap_new(); + this->heap.reset(hp, (_mi_destroy ? &heap_destroy : &heap_delete)); /* calls heap_delete/destroy when the refcount drops to zero */ + } + _mi_heap_stl_allocator_common(const _mi_heap_stl_allocator_common& x) mi_attr_noexcept : heap(x.heap) { } + template _mi_heap_stl_allocator_common(const _mi_heap_stl_allocator_common& x) mi_attr_noexcept : heap(x.heap) { } + +private: + static void heap_delete(mi_heap_t* hp) { if (hp != NULL) { mi_heap_delete(hp); } } + static void heap_destroy(mi_heap_t* hp) { if (hp != NULL) { mi_heap_destroy(hp); } } +}; + +// STL allocator allocation in a specific heap +template struct mi_heap_stl_allocator : public _mi_heap_stl_allocator_common { + using typename _mi_heap_stl_allocator_common::size_type; + mi_heap_stl_allocator() : _mi_heap_stl_allocator_common() { } // creates fresh heap that is deleted when the destructor is called + mi_heap_stl_allocator(mi_heap_t* hp) : _mi_heap_stl_allocator_common(hp) { } // no delete nor destroy on the passed in heap + template mi_heap_stl_allocator(const mi_heap_stl_allocator& x) mi_attr_noexcept : _mi_heap_stl_allocator_common(x) { } + + mi_heap_stl_allocator select_on_container_copy_construction() const { return *this; } + void deallocate(T* p, size_type) { mi_free(p); } + template struct rebind { typedef mi_heap_stl_allocator other; }; +}; + +template bool operator==(const mi_heap_stl_allocator& x, const mi_heap_stl_allocator& y) mi_attr_noexcept { return (x.is_equal(y)); } +template bool operator!=(const mi_heap_stl_allocator& x, const mi_heap_stl_allocator& y) mi_attr_noexcept { return (!x.is_equal(y)); } + + +// STL allocator allocation in a specific heap, where `free` does nothing and +// the heap is destroyed in one go on destruction -- use with care! +template struct mi_heap_destroy_stl_allocator : public _mi_heap_stl_allocator_common { + using typename _mi_heap_stl_allocator_common::size_type; + mi_heap_destroy_stl_allocator() : _mi_heap_stl_allocator_common() { } // creates fresh heap that is destroyed when the destructor is called + mi_heap_destroy_stl_allocator(mi_heap_t* hp) : _mi_heap_stl_allocator_common(hp) { } // no delete nor destroy on the passed in heap + template mi_heap_destroy_stl_allocator(const mi_heap_destroy_stl_allocator& x) mi_attr_noexcept : _mi_heap_stl_allocator_common(x) { } + + mi_heap_destroy_stl_allocator select_on_container_copy_construction() const { return *this; } + void deallocate(T*, size_type) { /* do nothing as we destroy the heap on destruct. */ } + template struct rebind { typedef mi_heap_destroy_stl_allocator other; }; +}; + +template bool operator==(const mi_heap_destroy_stl_allocator& x, const mi_heap_destroy_stl_allocator& y) mi_attr_noexcept { return (x.is_equal(y)); } +template bool operator!=(const mi_heap_destroy_stl_allocator& x, const mi_heap_destroy_stl_allocator& y) mi_attr_noexcept { return (!x.is_equal(y)); } + +#endif // C++11 + #endif // __cplusplus #endif diff --git a/Source/mimalloc/include/mimalloc-atomic.h b/Source/mimalloc/include/mimalloc/atomic.h similarity index 94% rename from Source/mimalloc/include/mimalloc-atomic.h rename to Source/mimalloc/include/mimalloc/atomic.h index dc48f0a2f..fe79fbcaf 100644 --- a/Source/mimalloc/include/mimalloc-atomic.h +++ b/Source/mimalloc/include/mimalloc/atomic.h @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021 Microsoft Research, Daan Leijen +Copyright (c) 2018-2023 Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -11,9 +11,9 @@ terms of the MIT license. A copy of the license can be found in the file // -------------------------------------------------------------------------------------------- // Atomics // We need to be portable between C, C++, and MSVC. -// We base the primitives on the C/C++ atomics and create a mimimal wrapper for MSVC in C compilation mode. -// This is why we try to use only `uintptr_t` and `*` as atomic types. -// To gain better insight in the range of used atomics, we use explicitly named memory order operations +// We base the primitives on the C/C++ atomics and create a mimimal wrapper for MSVC in C compilation mode. +// This is why we try to use only `uintptr_t` and `*` as atomic types. +// To gain better insight in the range of used atomics, we use explicitly named memory order operations // instead of passing the memory order as a parameter. // ----------------------------------------------------------------------------------------------- @@ -23,10 +23,15 @@ terms of the MIT license. A copy of the license can be found in the file #define _Atomic(tp) std::atomic #define mi_atomic(name) std::atomic_##name #define mi_memory_order(name) std::memory_order_##name +#if !defined(ATOMIC_VAR_INIT) || (__cplusplus >= 202002L) // c++20, see issue #571 + #define MI_ATOMIC_VAR_INIT(x) x +#else + #define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x) +#endif #elif defined(_MSC_VER) // Use MSVC C wrapper for C11 atomics #define _Atomic(tp) tp -#define ATOMIC_VAR_INIT(x) x +#define MI_ATOMIC_VAR_INIT(x) x #define mi_atomic(name) mi_atomic_##name #define mi_memory_order(name) mi_memory_order_##name #else @@ -34,6 +39,7 @@ terms of the MIT license. A copy of the license can be found in the file #include #define mi_atomic(name) atomic_##name #define mi_memory_order(name) memory_order_##name +#define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x) #endif // Various defines for all used memory orders in mimalloc @@ -173,7 +179,7 @@ static inline uintptr_t mi_atomic_exchange_explicit(_Atomic(uintptr_t)*p, uintpt } static inline void mi_atomic_thread_fence(mi_memory_order mo) { (void)(mo); - _Atomic(uintptr_t)x = 0; + _Atomic(uintptr_t) x = 0; mi_atomic_exchange_explicit(&x, 1, mo); } static inline uintptr_t mi_atomic_load_explicit(_Atomic(uintptr_t) const* p, mi_memory_order mo) { @@ -269,7 +275,16 @@ static inline intptr_t mi_atomic_subi(_Atomic(intptr_t)*p, intptr_t sub) { return (intptr_t)mi_atomic_addi(p, -sub); } -// Yield +typedef _Atomic(uintptr_t) mi_atomic_once_t; + +// Returns true only on the first invocation +static inline bool mi_atomic_once( mi_atomic_once_t* once ) { + if (mi_atomic_load_relaxed(once) != 0) return false; // quick test + uintptr_t expected = 0; + return mi_atomic_cas_strong_acq_rel(once, &expected, 1); // try to set to 1 +} + +// Yield #if defined(__cplusplus) #include static inline void mi_atomic_yield(void) { diff --git a/Source/mimalloc/include/mimalloc-internal.h b/Source/mimalloc/include/mimalloc/internal.h similarity index 64% rename from Source/mimalloc/include/mimalloc-internal.h rename to Source/mimalloc/include/mimalloc/internal.h index 1e1a79665..a4495c161 100644 --- a/Source/mimalloc/include/mimalloc-internal.h +++ b/Source/mimalloc/include/mimalloc/internal.h @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2023, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -8,7 +8,14 @@ terms of the MIT license. A copy of the license can be found in the file #ifndef MIMALLOC_INTERNAL_H #define MIMALLOC_INTERNAL_H -#include "mimalloc-types.h" + +// -------------------------------------------------------------------------- +// This file contains the interal API's of mimalloc and various utility +// functions and macros. +// -------------------------------------------------------------------------- + +#include "mimalloc/types.h" +#include "mimalloc/track.h" #if (MI_DEBUG>0) #define mi_trace_message(...) _mi_trace_message(__VA_ARGS__) @@ -19,10 +26,11 @@ terms of the MIT license. A copy of the license can be found in the file #define MI_CACHE_LINE 64 #if defined(_MSC_VER) #pragma warning(disable:4127) // suppress constant conditional warning (due to MI_SECURE paths) +#pragma warning(disable:26812) // unscoped enum warning #define mi_decl_noinline __declspec(noinline) #define mi_decl_thread __declspec(thread) #define mi_decl_cache_align __declspec(align(MI_CACHE_LINE)) -#elif (defined(__GNUC__) && (__GNUC__>=3)) // includes clang and icc +#elif (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) // includes clang and icc #define mi_decl_noinline __attribute__((noinline)) #define mi_decl_thread __thread #define mi_decl_cache_align __attribute__((aligned(MI_CACHE_LINE))) @@ -32,6 +40,22 @@ terms of the MIT license. A copy of the license can be found in the file #define mi_decl_cache_align #endif +#if defined(__EMSCRIPTEN__) && !defined(__wasi__) +#define __wasi__ +#endif + +#if defined(__cplusplus) +#define mi_decl_externc extern "C" +#else +#define mi_decl_externc +#endif + +// pthreads +#if !defined(_WIN32) && !defined(__wasi__) +#define MI_USE_PTHREADS +#include +#endif + // "options.c" void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message); void _mi_fprintf(mi_output_fun* out, void* arg, const char* fmt, ...); @@ -43,61 +67,96 @@ void _mi_error_message(int err, const char* fmt, ...); // random.c void _mi_random_init(mi_random_ctx_t* ctx); +void _mi_random_init_weak(mi_random_ctx_t* ctx); +void _mi_random_reinit_if_weak(mi_random_ctx_t * ctx); void _mi_random_split(mi_random_ctx_t* ctx, mi_random_ctx_t* new_ctx); uintptr_t _mi_random_next(mi_random_ctx_t* ctx); uintptr_t _mi_heap_random_next(mi_heap_t* heap); -uintptr_t _os_random_weak(uintptr_t extra_seed); +uintptr_t _mi_os_random_weak(uintptr_t extra_seed); static inline uintptr_t _mi_random_shuffle(uintptr_t x); // init.c extern mi_decl_cache_align mi_stats_t _mi_stats_main; extern mi_decl_cache_align const mi_page_t _mi_page_empty; bool _mi_is_main_thread(void); -bool _mi_preloading(); // true while the C runtime is not ready +size_t _mi_current_thread_count(void); +bool _mi_preloading(void); // true while the C runtime is not ready +mi_threadid_t _mi_thread_id(void) mi_attr_noexcept; +mi_heap_t* _mi_heap_main_get(void); // statically allocated main backing heap +void _mi_thread_done(mi_heap_t* heap); // os.c -size_t _mi_os_page_size(void); void _mi_os_init(void); // called from process init void* _mi_os_alloc(size_t size, mi_stats_t* stats); // to allocate thread local data void _mi_os_free(void* p, size_t size, mi_stats_t* stats); // to free thread local data +size_t _mi_os_page_size(void); size_t _mi_os_good_alloc_size(size_t size); - -// memory.c -void* _mi_mem_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* id, mi_os_tld_t* tld); -void _mi_mem_free(void* p, size_t size, size_t id, bool fully_committed, bool any_reset, mi_os_tld_t* tld); - -bool _mi_mem_reset(void* p, size_t size, mi_os_tld_t* tld); -bool _mi_mem_unreset(void* p, size_t size, bool* is_zero, mi_os_tld_t* tld); -bool _mi_mem_commit(void* p, size_t size, bool* is_zero, mi_os_tld_t* tld); -bool _mi_mem_protect(void* addr, size_t size); -bool _mi_mem_unprotect(void* addr, size_t size); - -void _mi_mem_collect(mi_os_tld_t* tld); +bool _mi_os_has_overcommit(void); + +bool _mi_os_reset(void* addr, size_t size, mi_stats_t* tld_stats); +bool _mi_os_commit(void* p, size_t size, bool* is_zero, mi_stats_t* stats); +bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats); +bool _mi_os_protect(void* addr, size_t size); +bool _mi_os_unprotect(void* addr, size_t size); + +void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool* large, mi_stats_t* stats); +void* _mi_os_alloc_aligned_offset(size_t size, size_t alignment, size_t align_offset, bool commit, bool* large, mi_stats_t* tld_stats); +void _mi_os_free_aligned(void* p, size_t size, size_t alignment, size_t align_offset, bool was_committed, mi_stats_t* tld_stats); +void* _mi_os_get_aligned_hint(size_t try_alignment, size_t size); +bool _mi_os_use_large_page(size_t size, size_t alignment); +size_t _mi_os_large_page_size(void); + +void _mi_os_free_ex(void* p, size_t size, bool was_committed, mi_stats_t* stats); +void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_secs, size_t* pages_reserved, size_t* psize); +void _mi_os_free_huge_pages(void* p, size_t size, mi_stats_t* stats); + +// arena.c +mi_arena_id_t _mi_arena_id_none(void); +void _mi_arena_free(void* p, size_t size, size_t alignment, size_t align_offset, size_t memid, bool all_committed, mi_stats_t* stats); +void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld); +void* _mi_arena_alloc_aligned(size_t size, size_t alignment, size_t align_offset, bool* commit, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld); +bool _mi_arena_memid_is_suitable(size_t arena_memid, mi_arena_id_t request_arena_id); +bool _mi_arena_is_os_allocated(size_t arena_memid); + +// "segment-cache.c" +void* _mi_segment_cache_pop(size_t size, mi_commit_mask_t* commit_mask, mi_commit_mask_t* decommit_mask, bool large_allowed, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld); +bool _mi_segment_cache_push(void* start, size_t size, size_t memid, const mi_commit_mask_t* commit_mask, const mi_commit_mask_t* decommit_mask, bool is_large, bool is_pinned, mi_os_tld_t* tld); +void _mi_segment_cache_collect(bool force, mi_os_tld_t* tld); +void _mi_segment_cache_free_all(mi_os_tld_t* tld); +void _mi_segment_map_allocated_at(const mi_segment_t* segment); +void _mi_segment_map_freed_at(const mi_segment_t* segment); // "segment.c" -mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_wsize, mi_segments_tld_t* tld, mi_os_tld_t* os_tld); +mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, size_t page_alignment, mi_segments_tld_t* tld, mi_os_tld_t* os_tld); void _mi_segment_page_free(mi_page_t* page, bool force, mi_segments_tld_t* tld); void _mi_segment_page_abandon(mi_page_t* page, mi_segments_tld_t* tld); -uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t block_size, size_t* page_size, size_t* pre_size); // page start for any page +bool _mi_segment_try_reclaim_abandoned( mi_heap_t* heap, bool try_all, mi_segments_tld_t* tld); +void _mi_segment_thread_collect(mi_segments_tld_t* tld); + +#if MI_HUGE_PAGE_ABANDON void _mi_segment_huge_page_free(mi_segment_t* segment, mi_page_t* page, mi_block_t* block); +#else +void _mi_segment_huge_page_reset(mi_segment_t* segment, mi_page_t* page, mi_block_t* block); +#endif -void _mi_segment_thread_collect(mi_segments_tld_t* tld); +uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size); // page start for any page void _mi_abandoned_reclaim_all(mi_heap_t* heap, mi_segments_tld_t* tld); void _mi_abandoned_await_readers(void); - - +void _mi_abandoned_collect(mi_heap_t* heap, bool force, mi_segments_tld_t* tld); // "page.c" -void* _mi_malloc_generic(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc; +void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept mi_attr_malloc; -void _mi_page_retire(mi_page_t* page); // free the page if there are no other pages with many free blocks +void _mi_page_retire(mi_page_t* page) mi_attr_noexcept; // free the page if there are no other pages with many free blocks void _mi_page_unfull(mi_page_t* page); void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force); // free the page void _mi_page_abandon(mi_page_t* page, mi_page_queue_t* pq); // abandon the page, to be picked up by another thread... -void _mi_heap_delayed_free(mi_heap_t* heap); +void _mi_heap_delayed_free_all(mi_heap_t* heap); +bool _mi_heap_delayed_free_partial(mi_heap_t* heap); void _mi_heap_collect_retired(mi_heap_t* heap, bool force); void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never); +bool _mi_page_try_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never); size_t _mi_page_queue_append(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_queue_t* append); void _mi_deferred_free(mi_heap_t* heap, bool force); @@ -111,21 +170,33 @@ uint8_t _mi_bin(size_t size); // for stats void _mi_heap_destroy_pages(mi_heap_t* heap); void _mi_heap_collect_abandon(mi_heap_t* heap); void _mi_heap_set_default_direct(mi_heap_t* heap); +bool _mi_heap_memid_is_suitable(mi_heap_t* heap, size_t memid); +void _mi_heap_destroy_all(void); // "stats.c" void _mi_stats_done(mi_stats_t* stats); - mi_msecs_t _mi_clock_now(void); mi_msecs_t _mi_clock_end(mi_msecs_t start); mi_msecs_t _mi_clock_start(void); // "alloc.c" -void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept; // called from `_mi_malloc_generic` -void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero); -void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero); +void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept; // called from `_mi_malloc_generic` +void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept; +void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept; // called from `_mi_heap_malloc_aligned` +void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) mi_attr_noexcept; mi_block_t* _mi_page_ptr_unalign(const mi_segment_t* segment, const mi_page_t* page, const void* p); bool _mi_free_delayed_block(mi_block_t* block); -void _mi_block_zero_init(const mi_page_t* page, void* p, size_t size); +void _mi_free_generic(const mi_segment_t* segment, mi_page_t* page, bool is_local, void* p) mi_attr_noexcept; // for runtime integration +void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size); + +// option.c, c primitives +char _mi_toupper(char c); +int _mi_strnicmp(const char* s, const char* t, size_t n); +void _mi_strlcpy(char* dest, const char* src, size_t dest_size); +void _mi_strlcat(char* dest, const char* src, size_t dest_size); +size_t _mi_strlen(const char* s); +size_t _mi_strnlen(const char* s, size_t max_len); + #if MI_DEBUG>1 bool _mi_page_is_valid(mi_page_t* page); @@ -137,8 +208,11 @@ bool _mi_page_is_valid(mi_page_t* page); // ------------------------------------------------------ #if defined(__GNUC__) || defined(__clang__) -#define mi_unlikely(x) __builtin_expect((x),0) -#define mi_likely(x) __builtin_expect((x),1) +#define mi_unlikely(x) (__builtin_expect(!!(x),false)) +#define mi_likely(x) (__builtin_expect(!!(x),true)) +#elif (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +#define mi_unlikely(x) (x) [[unlikely]] +#define mi_likely(x) (x) [[likely]] #else #define mi_unlikely(x) (x) #define mi_likely(x) (x) @@ -176,11 +250,11 @@ bool _mi_page_is_valid(mi_page_t* page); /* ----------------------------------------------------------- Inlined definitions ----------------------------------------------------------- */ -#define UNUSED(x) (void)(x) +#define MI_UNUSED(x) (void)(x) #if (MI_DEBUG>0) -#define UNUSED_RELEASE(x) +#define MI_UNUSED_RELEASE(x) #else -#define UNUSED_RELEASE(x) UNUSED(x) +#define MI_UNUSED_RELEASE(x) MI_UNUSED(x) #endif #define MI_INIT4(x) x(),x(),x(),x() @@ -197,6 +271,12 @@ static inline bool _mi_is_power_of_two(uintptr_t x) { return ((x & (x - 1)) == 0); } +// Is a pointer aligned? +static inline bool _mi_is_aligned(void* p, size_t alignment) { + mi_assert_internal(alignment != 0); + return (((uintptr_t)p % alignment) == 0); +} + // Align upwards static inline uintptr_t _mi_align_up(uintptr_t sz, size_t alignment) { mi_assert_internal(alignment != 0); @@ -209,6 +289,18 @@ static inline uintptr_t _mi_align_up(uintptr_t sz, size_t alignment) { } } +// Align downwards +static inline uintptr_t _mi_align_down(uintptr_t sz, size_t alignment) { + mi_assert_internal(alignment != 0); + uintptr_t mask = alignment - 1; + if ((alignment & mask) == 0) { // power of two? + return (sz & ~mask); + } + else { + return ((sz / alignment) * alignment); + } +} + // Divide upwards: `s <= _mi_divide_up(s,d)*d < s+d`. static inline uintptr_t _mi_divide_up(uintptr_t size, size_t divider) { mi_assert_internal(divider != 0); @@ -223,6 +315,7 @@ static inline bool mi_mem_is_zero(void* p, size_t size) { return true; } + // Align a byte size to a size in _machine words_, // i.e. byte size == `wsize*sizeof(void*)`. static inline size_t _mi_wsize_from_size(size_t size) { @@ -230,32 +323,27 @@ static inline size_t _mi_wsize_from_size(size_t size) { return (size + sizeof(uintptr_t) - 1) / sizeof(uintptr_t); } -// Does malloc satisfy the alignment constraints already? -static inline bool mi_malloc_satisfies_alignment(size_t alignment, size_t size) { - return (alignment == sizeof(void*) || (alignment == MI_MAX_ALIGN_SIZE && size > (MI_MAX_ALIGN_SIZE/2))); -} - // Overflow detecting multiply -#if __has_builtin(__builtin_umul_overflow) || __GNUC__ >= 5 +#if __has_builtin(__builtin_umul_overflow) || (defined(__GNUC__) && (__GNUC__ >= 5)) #include // UINT_MAX, ULONG_MAX #if defined(_CLOCK_T) // for Illumos #undef _CLOCK_T #endif static inline bool mi_mul_overflow(size_t count, size_t size, size_t* total) { - #if (SIZE_MAX == UINT_MAX) - return __builtin_umul_overflow(count, size, total); - #elif (SIZE_MAX == ULONG_MAX) - return __builtin_umull_overflow(count, size, total); + #if (SIZE_MAX == ULONG_MAX) + return __builtin_umull_overflow(count, size, (unsigned long *)total); + #elif (SIZE_MAX == UINT_MAX) + return __builtin_umul_overflow(count, size, (unsigned int *)total); #else - return __builtin_umulll_overflow(count, size, total); + return __builtin_umulll_overflow(count, size, (unsigned long long *)total); #endif } #else /* __builtin_umul_overflow is unavailable */ static inline bool mi_mul_overflow(size_t count, size_t size, size_t* total) { #define MI_MUL_NO_OVERFLOW ((size_t)1 << (4*sizeof(size_t))) // sqrt(SIZE_MAX) *total = count * size; - return ((size >= MI_MUL_NO_OVERFLOW || count >= MI_MUL_NO_OVERFLOW) - && size > 0 && (SIZE_MAX / size) < count); + // note: gcc/clang optimize this to directly check the overflow flag + return ((size >= MI_MUL_NO_OVERFLOW || count >= MI_MUL_NO_OVERFLOW) && size > 0 && (SIZE_MAX / size) < count); } #endif @@ -265,8 +353,10 @@ static inline bool mi_count_size_overflow(size_t count, size_t size, size_t* tot *total = size; return false; } - else if (mi_unlikely(mi_mul_overflow(count, size, total))) { + else if mi_unlikely(mi_mul_overflow(count, size, total)) { + #if MI_DEBUG > 0 _mi_error_message(EOVERFLOW, "allocation request is too large (%zu * %zu bytes)\n", count, size); + #endif *total = SIZE_MAX; return true; } @@ -274,85 +364,11 @@ static inline bool mi_count_size_overflow(size_t count, size_t size, size_t* tot } -/* ---------------------------------------------------------------------------------------- -The thread local default heap: `_mi_get_default_heap` returns the thread local heap. -On most platforms (Windows, Linux, FreeBSD, NetBSD, etc), this just returns a -__thread local variable (`_mi_heap_default`). With the initial-exec TLS model this ensures -that the storage will always be available (allocated on the thread stacks). -On some platforms though we cannot use that when overriding `malloc` since the underlying -TLS implementation (or the loader) will call itself `malloc` on a first access and recurse. -We try to circumvent this in an efficient way: -- macOSX : we use an unused TLS slot from the OS allocated slots (MI_TLS_SLOT). On OSX, the - loader itself calls `malloc` even before the modules are initialized. -- OpenBSD: we use an unused slot from the pthread block (MI_TLS_PTHREAD_SLOT_OFS). -- DragonFly: the uniqueid use is buggy but kept for reference. +/*---------------------------------------------------------------------------------------- + Heap functions ------------------------------------------------------------------------------------------- */ extern const mi_heap_t _mi_heap_empty; // read-only empty heap, initial value of the thread local default heap -extern bool _mi_process_is_initialized; -mi_heap_t* _mi_heap_main_get(void); // statically allocated main backing heap - -#if defined(MI_MALLOC_OVERRIDE) -#if defined(__APPLE__) // macOS -#define MI_TLS_SLOT 89 // seems unused? -// other possible unused ones are 9, 29, __PTK_FRAMEWORK_JAVASCRIPTCORE_KEY4 (94), __PTK_FRAMEWORK_GC_KEY9 (112) and __PTK_FRAMEWORK_OLDGC_KEY9 (89) -// see -#elif defined(__OpenBSD__) -// use end bytes of a name; goes wrong if anyone uses names > 23 characters (ptrhread specifies 16) -// see -#define MI_TLS_PTHREAD_SLOT_OFS (6*sizeof(int) + 4*sizeof(void*) + 24) -#elif defined(__DragonFly__) -#warning "mimalloc is not working correctly on DragonFly yet." -//#define MI_TLS_PTHREAD_SLOT_OFS (4 + 1*sizeof(void*)) // offset `uniqueid` (also used by gdb?) -#endif -#endif - -#if defined(MI_TLS_SLOT) -static inline void* mi_tls_slot(size_t slot) mi_attr_noexcept; // forward declaration -#elif defined(MI_TLS_PTHREAD_SLOT_OFS) -#include -static inline mi_heap_t** mi_tls_pthread_heap_slot(void) { - pthread_t self = pthread_self(); - #if defined(__DragonFly__) - if (self==NULL) { - mi_heap_t* pheap_main = _mi_heap_main_get(); - return &pheap_main; - } - #endif - return (mi_heap_t**)((uint8_t*)self + MI_TLS_PTHREAD_SLOT_OFS); -} -#elif defined(MI_TLS_PTHREAD) -#include -extern pthread_key_t _mi_heap_default_key; -#endif - -// Default heap to allocate from (if not using TLS- or pthread slots). -// Do not use this directly but use through `mi_heap_get_default()` (or the unchecked `mi_get_default_heap`). -// This thread local variable is only used when neither MI_TLS_SLOT, MI_TLS_PTHREAD, or MI_TLS_PTHREAD_SLOT_OFS are defined. -// However, on the Apple M1 we do use the address of this variable as the unique thread-id (issue #356). -extern mi_decl_thread mi_heap_t* _mi_heap_default; // default heap to allocate from - -static inline mi_heap_t* mi_get_default_heap(void) { -#if defined(MI_TLS_SLOT) - mi_heap_t* heap = (mi_heap_t*)mi_tls_slot(MI_TLS_SLOT); - return (mi_unlikely(heap == NULL) ? (mi_heap_t*)&_mi_heap_empty : heap); -#elif defined(MI_TLS_PTHREAD_SLOT_OFS) - mi_heap_t* heap = *mi_tls_pthread_heap_slot(); - return (mi_unlikely(heap == NULL) ? (mi_heap_t*)&_mi_heap_empty : heap); -#elif defined(MI_TLS_PTHREAD) - mi_heap_t* heap = (mi_unlikely(_mi_heap_default_key == (pthread_key_t)(-1)) ? _mi_heap_main_get() : (mi_heap_t*)pthread_getspecific(_mi_heap_default_key)); - return (mi_unlikely(heap == NULL) ? (mi_heap_t*)&_mi_heap_empty : heap); -#else - #if defined(MI_TLS_RECURSE_GUARD) - if (mi_unlikely(!_mi_process_is_initialized)) return _mi_heap_main_get(); - #endif - return _mi_heap_default; -#endif -} - -static inline bool mi_heap_is_default(const mi_heap_t* heap) { - return (heap == mi_get_default_heap()); -} static inline bool mi_heap_is_backing(const mi_heap_t* heap) { return (heap->tld->heap_backing == heap); @@ -380,46 +396,57 @@ static inline mi_page_t* _mi_heap_get_free_small_page(mi_heap_t* heap, size_t si return heap->pages_free_direct[idx]; } -// Get the page belonging to a certain size class -static inline mi_page_t* _mi_get_free_small_page(size_t size) { - return _mi_heap_get_free_small_page(mi_get_default_heap(), size); -} - // Segment that contains the pointer +// Large aligned blocks may be aligned at N*MI_SEGMENT_SIZE (inside a huge segment > MI_SEGMENT_SIZE), +// and we need align "down" to the segment info which is `MI_SEGMENT_SIZE` bytes before it; +// therefore we align one byte before `p`. static inline mi_segment_t* _mi_ptr_segment(const void* p) { - // mi_assert_internal(p != NULL); - return (mi_segment_t*)((uintptr_t)p & ~MI_SEGMENT_MASK); + mi_assert_internal(p != NULL); + return (mi_segment_t*)(((uintptr_t)p - 1) & ~MI_SEGMENT_MASK); +} + +static inline mi_page_t* mi_slice_to_page(mi_slice_t* s) { + mi_assert_internal(s->slice_offset== 0 && s->slice_count > 0); + return (mi_page_t*)(s); +} + +static inline mi_slice_t* mi_page_to_slice(mi_page_t* p) { + mi_assert_internal(p->slice_offset== 0 && p->slice_count > 0); + return (mi_slice_t*)(p); } // Segment belonging to a page static inline mi_segment_t* _mi_page_segment(const mi_page_t* page) { - mi_segment_t* segment = _mi_ptr_segment(page); - mi_assert_internal(segment == NULL || page == &segment->pages[page->segment_idx]); + mi_segment_t* segment = _mi_ptr_segment(page); + mi_assert_internal(segment == NULL || ((mi_slice_t*)page >= segment->slices && (mi_slice_t*)page < segment->slices + segment->slice_entries)); return segment; } -// used internally -static inline uintptr_t _mi_segment_page_idx_of(const mi_segment_t* segment, const void* p) { - // if (segment->page_size > MI_SEGMENT_SIZE) return &segment->pages[0]; // huge pages - ptrdiff_t diff = (uint8_t*)p - (uint8_t*)segment; - mi_assert_internal(diff >= 0 && (size_t)diff < MI_SEGMENT_SIZE); - uintptr_t idx = (uintptr_t)diff >> segment->page_shift; - mi_assert_internal(idx < segment->capacity); - mi_assert_internal(segment->page_kind <= MI_PAGE_MEDIUM || idx == 0); - return idx; +static inline mi_slice_t* mi_slice_first(const mi_slice_t* slice) { + mi_slice_t* start = (mi_slice_t*)((uint8_t*)slice - slice->slice_offset); + mi_assert_internal(start >= _mi_ptr_segment(slice)->slices); + mi_assert_internal(start->slice_offset == 0); + mi_assert_internal(start + start->slice_count > slice); + return start; } -// Get the page containing the pointer +// Get the page containing the pointer (performance critical as it is called in mi_free) static inline mi_page_t* _mi_segment_page_of(const mi_segment_t* segment, const void* p) { - uintptr_t idx = _mi_segment_page_idx_of(segment, p); - return &((mi_segment_t*)segment)->pages[idx]; + mi_assert_internal(p > (void*)segment); + ptrdiff_t diff = (uint8_t*)p - (uint8_t*)segment; + mi_assert_internal(diff > 0 && diff <= (ptrdiff_t)MI_SEGMENT_SIZE); + size_t idx = (size_t)diff >> MI_SEGMENT_SLICE_SHIFT; + mi_assert_internal(idx <= segment->slice_entries); + mi_slice_t* slice0 = (mi_slice_t*)&segment->slices[idx]; + mi_slice_t* slice = mi_slice_first(slice0); // adjust to the block that holds the page data + mi_assert_internal(slice->slice_offset == 0); + mi_assert_internal(slice >= segment->slices && slice < segment->slices + segment->slice_entries); + return mi_slice_to_page(slice); } // Quick page start for initialized pages static inline uint8_t* _mi_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size) { - const size_t bsize = page->xblock_size; - mi_assert_internal(bsize > 0 && (bsize%sizeof(void*)) == 0); - return _mi_segment_page_start(segment, page, bsize, page_size, NULL); + return _mi_segment_page_start(segment, page, page_size); } // Get the page containing the pointer @@ -427,26 +454,38 @@ static inline mi_page_t* _mi_ptr_page(void* p) { return _mi_segment_page_of(_mi_ptr_segment(p), p); } -// Get the block size of a page (special cased for huge objects) +// Get the block size of a page (special case for huge objects) static inline size_t mi_page_block_size(const mi_page_t* page) { const size_t bsize = page->xblock_size; mi_assert_internal(bsize > 0); - if (mi_likely(bsize < MI_HUGE_BLOCK_SIZE)) { + if mi_likely(bsize < MI_HUGE_BLOCK_SIZE) { return bsize; } else { size_t psize; - _mi_segment_page_start(_mi_page_segment(page), page, bsize, &psize, NULL); + _mi_segment_page_start(_mi_page_segment(page), page, &psize); return psize; } } +static inline bool mi_page_is_huge(const mi_page_t* page) { + return (_mi_page_segment(page)->kind == MI_SEGMENT_HUGE); +} + // Get the usable block size of a page without fixed padding. // This may still include internal padding due to alignment and rounding up size classes. static inline size_t mi_page_usable_block_size(const mi_page_t* page) { return mi_page_block_size(page) - MI_PADDING_SIZE; } +// size of a segment +static inline size_t mi_segment_size(mi_segment_t* segment) { + return segment->segment_slices * MI_SEGMENT_SLICE_SIZE; +} + +static inline uint8_t* mi_segment_end(mi_segment_t* segment) { + return (uint8_t*)segment + mi_segment_size(segment); +} // Thread free access static inline mi_block_t* mi_page_thread_free(const mi_page_t* page) { @@ -566,12 +605,13 @@ static inline bool mi_is_in_same_segment(const void* p, const void* q) { } static inline bool mi_is_in_same_page(const void* p, const void* q) { - mi_segment_t* segmentp = _mi_ptr_segment(p); - mi_segment_t* segmentq = _mi_ptr_segment(q); - if (segmentp != segmentq) return false; - uintptr_t idxp = _mi_segment_page_idx_of(segmentp, p); - uintptr_t idxq = _mi_segment_page_idx_of(segmentq, q); - return (idxp == idxq); + mi_segment_t* segment = _mi_ptr_segment(p); + if (_mi_ptr_segment(q) != segment) return false; + // assume q may be invalid // return (_mi_segment_page_of(segment, p) == _mi_segment_page_of(segment, q)); + mi_page_t* page = _mi_segment_page_of(segment, p); + size_t psize; + uint8_t* start = _mi_segment_page_start(segment, page, &psize); + return (start <= (uint8_t*)q && (uint8_t*)q < start + psize); } static inline uintptr_t mi_rotl(uintptr_t x, uintptr_t shift) { @@ -585,30 +625,36 @@ static inline uintptr_t mi_rotr(uintptr_t x, uintptr_t shift) { static inline void* mi_ptr_decode(const void* null, const mi_encoded_t x, const uintptr_t* keys) { void* p = (void*)(mi_rotr(x - keys[0], keys[0]) ^ keys[1]); - return (mi_unlikely(p==null) ? NULL : p); + return (p==null ? NULL : p); } static inline mi_encoded_t mi_ptr_encode(const void* null, const void* p, const uintptr_t* keys) { - uintptr_t x = (uintptr_t)(mi_unlikely(p==NULL) ? null : p); + uintptr_t x = (uintptr_t)(p==NULL ? null : p); return mi_rotl(x ^ keys[1], keys[0]) + keys[0]; } static inline mi_block_t* mi_block_nextx( const void* null, const mi_block_t* block, const uintptr_t* keys ) { + mi_track_mem_defined(block,sizeof(mi_block_t)); + mi_block_t* next; #ifdef MI_ENCODE_FREELIST - return (mi_block_t*)mi_ptr_decode(null, block->next, keys); + next = (mi_block_t*)mi_ptr_decode(null, block->next, keys); #else - UNUSED(keys); UNUSED(null); - return (mi_block_t*)block->next; + MI_UNUSED(keys); MI_UNUSED(null); + next = (mi_block_t*)block->next; #endif + mi_track_mem_noaccess(block,sizeof(mi_block_t)); + return next; } static inline void mi_block_set_nextx(const void* null, mi_block_t* block, const mi_block_t* next, const uintptr_t* keys) { + mi_track_mem_undefined(block,sizeof(mi_block_t)); #ifdef MI_ENCODE_FREELIST block->next = mi_ptr_encode(null, next, keys); #else - UNUSED(keys); UNUSED(null); + MI_UNUSED(keys); MI_UNUSED(null); block->next = (mi_encoded_t)next; #endif + mi_track_mem_noaccess(block,sizeof(mi_block_t)); } static inline mi_block_t* mi_block_next(const mi_page_t* page, const mi_block_t* block) { @@ -616,13 +662,13 @@ static inline mi_block_t* mi_block_next(const mi_page_t* page, const mi_block_t* mi_block_t* next = mi_block_nextx(page,block,page->keys); // check for free list corruption: is `next` at least in the same page? // TODO: check if `next` is `page->block_size` aligned? - if (mi_unlikely(next!=NULL && !mi_is_in_same_page(block, next))) { + if mi_unlikely(next!=NULL && !mi_is_in_same_page(block, next)) { _mi_error_message(EFAULT, "corrupted free list entry of size %zub at %p: value 0x%zx\n", mi_page_block_size(page), block, (uintptr_t)next); next = NULL; } return next; #else - UNUSED(page); + MI_UNUSED(page); return mi_block_nextx(page,block,NULL); #endif } @@ -631,11 +677,57 @@ static inline void mi_block_set_next(const mi_page_t* page, mi_block_t* block, c #ifdef MI_ENCODE_FREELIST mi_block_set_nextx(page,block,next, page->keys); #else - UNUSED(page); + MI_UNUSED(page); mi_block_set_nextx(page,block,next,NULL); #endif } + +// ------------------------------------------------------------------- +// commit mask +// ------------------------------------------------------------------- + +static inline void mi_commit_mask_create_empty(mi_commit_mask_t* cm) { + for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { + cm->mask[i] = 0; + } +} + +static inline void mi_commit_mask_create_full(mi_commit_mask_t* cm) { + for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { + cm->mask[i] = ~((size_t)0); + } +} + +static inline bool mi_commit_mask_is_empty(const mi_commit_mask_t* cm) { + for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { + if (cm->mask[i] != 0) return false; + } + return true; +} + +static inline bool mi_commit_mask_is_full(const mi_commit_mask_t* cm) { + for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { + if (cm->mask[i] != ~((size_t)0)) return false; + } + return true; +} + +// defined in `segment.c`: +size_t _mi_commit_mask_committed_size(const mi_commit_mask_t* cm, size_t total); +size_t _mi_commit_mask_next_run(const mi_commit_mask_t* cm, size_t* idx); + +#define mi_commit_mask_foreach(cm,idx,count) \ + idx = 0; \ + while ((count = _mi_commit_mask_next_run(cm,&idx)) > 0) { + +#define mi_commit_mask_foreach_end() \ + idx += count; \ + } + + + + // ------------------------------------------------------------------- // Fast "random" shuffle // ------------------------------------------------------------------- @@ -669,102 +761,16 @@ size_t _mi_os_numa_node_count_get(void); extern _Atomic(size_t) _mi_numa_node_count; static inline int _mi_os_numa_node(mi_os_tld_t* tld) { - if (mi_likely(mi_atomic_load_relaxed(&_mi_numa_node_count) == 1)) return 0; + if mi_likely(mi_atomic_load_relaxed(&_mi_numa_node_count) == 1) { return 0; } else return _mi_os_numa_node_get(tld); } static inline size_t _mi_os_numa_node_count(void) { const size_t count = mi_atomic_load_relaxed(&_mi_numa_node_count); - if (mi_likely(count>0)) return count; + if mi_likely(count > 0) { return count; } else return _mi_os_numa_node_count_get(); } -// ------------------------------------------------------------------- -// Getting the thread id should be performant as it is called in the -// fast path of `_mi_free` and we specialize for various platforms. -// ------------------------------------------------------------------- -#if defined(_WIN32) -#define WIN32_LEAN_AND_MEAN -#include -static inline uintptr_t _mi_thread_id(void) mi_attr_noexcept { - // Windows: works on Intel and ARM in both 32- and 64-bit - return (uintptr_t)NtCurrentTeb(); -} - -#elif defined(__GNUC__) && \ - (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)) - -// TLS register on x86 is in the FS or GS register, see: https://akkadia.org/drepper/tls.pdf -static inline void* mi_tls_slot(size_t slot) mi_attr_noexcept { - void* res; - const size_t ofs = (slot*sizeof(void*)); -#if defined(__i386__) - __asm__("movl %%gs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // 32-bit always uses GS -#elif defined(__APPLE__) && defined(__x86_64__) - __asm__("movq %%gs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // x86_64 macOSX uses GS -#elif defined(__x86_64__) && (MI_INTPTR_SIZE==4) - __asm__("movl %%fs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // x32 ABI -#elif defined(__x86_64__) - __asm__("movq %%fs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // x86_64 Linux, BSD uses FS -#elif defined(__arm__) - void** tcb; UNUSED(ofs); - __asm__ volatile ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tcb)); - res = tcb[slot]; -#elif defined(__aarch64__) - void** tcb; UNUSED(ofs); -#if defined(__APPLE__) // M1, issue #343 - __asm__ volatile ("mrs %0, tpidrro_el0" : "=r" (tcb)); - tcb = (void**)((uintptr_t)tcb & ~0x07UL); // clear lower 3 bits -#else - __asm__ volatile ("mrs %0, tpidr_el0" : "=r" (tcb)); -#endif - res = tcb[slot]; -#endif - return res; -} - -// setting is only used on macOSX for now -static inline void mi_tls_slot_set(size_t slot, void* value) mi_attr_noexcept { - const size_t ofs = (slot*sizeof(void*)); -#if defined(__i386__) - __asm__("movl %1,%%gs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); // 32-bit always uses GS -#elif defined(__APPLE__) && defined(__x86_64__) - __asm__("movq %1,%%gs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); // x86_64 macOSX uses GS -#elif defined(__x86_64__) && (MI_INTPTR_SIZE==4) - __asm__("movl %1,%%fs:%1" : "=m" (*((void**)ofs)) : "rn" (value) : ); // x32 ABI -#elif defined(__x86_64__) - __asm__("movq %1,%%fs:%1" : "=m" (*((void**)ofs)) : "rn" (value) : ); // x86_64 Linux, BSD uses FS -#elif defined(__arm__) - void** tcb; UNUSED(ofs); - __asm__ volatile ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tcb)); - tcb[slot] = value; -#elif defined(__aarch64__) - void** tcb; UNUSED(ofs); -#if defined(__APPLE__) // M1, issue #343 - __asm__ volatile ("mrs %0, tpidrro_el0" : "=r" (tcb)); - tcb = (void**)((uintptr_t)tcb & ~0x07UL); // clear lower 3 bits -#else - __asm__ volatile ("mrs %0, tpidr_el0" : "=r" (tcb)); -#endif - tcb[slot] = value; -#endif -} - -static inline uintptr_t _mi_thread_id(void) mi_attr_noexcept { -#if defined(__BIONIC__) && (defined(__arm__) || defined(__aarch64__)) - // on Android, slot 1 is the thread ID (pointer to pthread internal struct) - return (uintptr_t)mi_tls_slot(1); -#else - // in all our other targets, slot 0 is the pointer to the thread control block - return (uintptr_t)mi_tls_slot(0); -#endif -} -#else -// otherwise use standard C -static inline uintptr_t _mi_thread_id(void) mi_attr_noexcept { - return (uintptr_t)&_mi_heap_default; -} -#endif // ----------------------------------------------------------------------- // Count bits: trailing or leading zeros (with MI_INTPTR_BITS on all zero) @@ -791,9 +797,10 @@ static inline size_t mi_ctz(uintptr_t x) { #endif } -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) #include // LONG_MAX +#include // BitScanReverse64 #define MI_HAVE_FAST_BITSCAN static inline size_t mi_clz(uintptr_t x) { if (x==0) return MI_INTPTR_BITS; @@ -802,7 +809,7 @@ static inline size_t mi_clz(uintptr_t x) { _BitScanReverse(&idx, x); #else _BitScanReverse64(&idx, x); -#endif +#endif return ((MI_INTPTR_BITS - 1) - idx); } static inline size_t mi_ctz(uintptr_t x) { @@ -812,7 +819,7 @@ static inline size_t mi_ctz(uintptr_t x) { _BitScanForward(&idx, x); #else _BitScanForward64(&idx, x); -#endif +#endif return idx; } @@ -842,7 +849,7 @@ static inline size_t mi_clz32(uint32_t x) { } static inline size_t mi_clz(uintptr_t x) { - if (x==0) return MI_INTPTR_BITS; + if (x==0) return MI_INTPTR_BITS; #if (MI_INTPTR_BITS <= 32) return mi_clz32((uint32_t)x); #else @@ -873,12 +880,12 @@ static inline size_t mi_bsr(uintptr_t x) { // --------------------------------------------------------------------------------- // Provide our own `_mi_memcpy` for potential performance optimizations. // -// For now, only on Windows with msvc/clang-cl we optimize to `rep movsb` if -// we happen to run on x86/x64 cpu's that have "fast short rep movsb" (FSRM) support -// (AMD Zen3+ (~2020) or Intel Ice Lake+ (~2017). See also issue #201 and pr #253. +// For now, only on Windows with msvc/clang-cl we optimize to `rep movsb` if +// we happen to run on x86/x64 cpu's that have "fast short rep movsb" (FSRM) support +// (AMD Zen3+ (~2020) or Intel Ice Lake+ (~2017). See also issue #201 and pr #253. // --------------------------------------------------------------------------------- -#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) +#if !MI_TRACK_ENABLED && defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) #include #include extern bool _mi_cpu_has_fsrm; @@ -887,7 +894,15 @@ static inline void _mi_memcpy(void* dst, const void* src, size_t n) { __movsb((unsigned char*)dst, (const unsigned char*)src, n); } else { - memcpy(dst, src, n); // todo: use noinline? + memcpy(dst, src, n); + } +} +static inline void _mi_memzero(void* dst, size_t n) { + if (_mi_cpu_has_fsrm) { + __stosb((unsigned char*)dst, 0, n); + } + else { + memset(dst, 0, n); } } #else @@ -895,22 +910,31 @@ static inline void _mi_memcpy(void* dst, const void* src, size_t n) { static inline void _mi_memcpy(void* dst, const void* src, size_t n) { memcpy(dst, src, n); } +static inline void _mi_memzero(void* dst, size_t n) { + memset(dst, 0, n); +} #endif // ------------------------------------------------------------------------------- -// The `_mi_memcpy_aligned` can be used if the pointers are machine-word aligned +// The `_mi_memcpy_aligned` can be used if the pointers are machine-word aligned // This is used for example in `mi_realloc`. // ------------------------------------------------------------------------------- -#if (__GNUC__ >= 4) || defined(__clang__) +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) // On GCC/CLang we provide a hint that the pointers are word aligned. #include static inline void _mi_memcpy_aligned(void* dst, const void* src, size_t n) { mi_assert_internal(((uintptr_t)dst % MI_INTPTR_SIZE == 0) && ((uintptr_t)src % MI_INTPTR_SIZE == 0)); void* adst = __builtin_assume_aligned(dst, MI_INTPTR_SIZE); const void* asrc = __builtin_assume_aligned(src, MI_INTPTR_SIZE); - memcpy(adst, asrc, n); + _mi_memcpy(adst, asrc, n); +} + +static inline void _mi_memzero_aligned(void* dst, size_t n) { + mi_assert_internal((uintptr_t)dst % MI_INTPTR_SIZE == 0); + void* adst = __builtin_assume_aligned(dst, MI_INTPTR_SIZE); + _mi_memzero(adst, n); } #else // Default fallback on `_mi_memcpy` @@ -918,6 +942,11 @@ static inline void _mi_memcpy_aligned(void* dst, const void* src, size_t n) { mi_assert_internal(((uintptr_t)dst % MI_INTPTR_SIZE == 0) && ((uintptr_t)src % MI_INTPTR_SIZE == 0)); _mi_memcpy(dst, src, n); } + +static inline void _mi_memzero_aligned(void* dst, size_t n) { + mi_assert_internal((uintptr_t)dst % MI_INTPTR_SIZE == 0); + _mi_memzero(dst, n); +} #endif diff --git a/Source/mimalloc/include/mimalloc/prim.h b/Source/mimalloc/include/mimalloc/prim.h new file mode 100644 index 000000000..10378c922 --- /dev/null +++ b/Source/mimalloc/include/mimalloc/prim.h @@ -0,0 +1,311 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ +#pragma once +#ifndef MIMALLOC_PRIM_H +#define MIMALLOC_PRIM_H + + +// -------------------------------------------------------------------------- +// This file specifies the primitive portability API. +// Each OS/host needs to implement these primitives, see `src/prim` +// for implementations on Window, macOS, WASI, and Linux/Unix. +// +// note: on all primitive functions, we always get: +// addr != NULL and page aligned +// size > 0 and page aligned +// return value is an error code an int where 0 is success. +// -------------------------------------------------------------------------- + +// OS memory configuration +typedef struct mi_os_mem_config_s { + size_t page_size; // 4KiB + size_t large_page_size; // 2MiB + size_t alloc_granularity; // smallest allocation size (on Windows 64KiB) + bool has_overcommit; // can we reserve more memory than can be actually committed? + bool must_free_whole; // must allocated blocks free as a whole (false for mmap, true for VirtualAlloc) +} mi_os_mem_config_t; + +// Initialize +void _mi_prim_mem_init( mi_os_mem_config_t* config ); + +// Free OS memory +int _mi_prim_free(void* addr, size_t size ); + +// Allocate OS memory. Return NULL on error. +// The `try_alignment` is just a hint and the returned pointer does not have to be aligned. +// pre: !commit => !allow_large +// try_alignment >= _mi_os_page_size() and a power of 2 +int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, void** addr); + +// Commit memory. Returns error code or 0 on success. +int _mi_prim_commit(void* addr, size_t size, bool commit); + +// Reset memory. The range keeps being accessible but the content might be reset. +// Returns error code or 0 on success. +int _mi_prim_reset(void* addr, size_t size); + +// Protect memory. Returns error code or 0 on success. +int _mi_prim_protect(void* addr, size_t size, bool protect); + +// Allocate huge (1GiB) pages possibly associated with a NUMA node. +// pre: size > 0 and a multiple of 1GiB. +// addr is either NULL or an address hint. +// numa_node is either negative (don't care), or a numa node number. +int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, void** addr); + +// Return the current NUMA node +size_t _mi_prim_numa_node(void); + +// Return the number of logical NUMA nodes +size_t _mi_prim_numa_node_count(void); + +// Clock ticks +mi_msecs_t _mi_prim_clock_now(void); + +// Return process information (only for statistics) +typedef struct mi_process_info_s { + mi_msecs_t elapsed; + mi_msecs_t utime; + mi_msecs_t stime; + size_t current_rss; + size_t peak_rss; + size_t current_commit; + size_t peak_commit; + size_t page_faults; +} mi_process_info_t; + +void _mi_prim_process_info(mi_process_info_t* pinfo); + +// Default stderr output. (only for warnings etc. with verbose enabled) +// msg != NULL && _mi_strlen(msg) > 0 +void _mi_prim_out_stderr( const char* msg ); + +// Get an environment variable. (only for options) +// name != NULL, result != NULL, result_size >= 64 +bool _mi_prim_getenv(const char* name, char* result, size_t result_size); + + +// Fill a buffer with strong randomness; return `false` on error or if +// there is no strong randomization available. +bool _mi_prim_random_buf(void* buf, size_t buf_len); + +// Called on the first thread start, and should ensure `_mi_thread_done` is called on thread termination. +void _mi_prim_thread_init_auto_done(void); + +// Called on process exit and may take action to clean up resources associated with the thread auto done. +void _mi_prim_thread_done_auto_done(void); + +// Called when the default heap for a thread changes +void _mi_prim_thread_associate_default_heap(mi_heap_t* heap); + + +//------------------------------------------------------------------- +// Thread id: `_mi_prim_thread_id()` +// +// Getting the thread id should be performant as it is called in the +// fast path of `_mi_free` and we specialize for various platforms as +// inlined definitions. Regular code should call `init.c:_mi_thread_id()`. +// We only require _mi_prim_thread_id() to return a unique id +// for each thread (unequal to zero). +//------------------------------------------------------------------- + +// defined in `init.c`; do not use these directly +extern mi_decl_thread mi_heap_t* _mi_heap_default; // default heap to allocate from +extern bool _mi_process_is_initialized; // has mi_process_init been called? + +static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept; + +#if defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#include +static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { + // Windows: works on Intel and ARM in both 32- and 64-bit + return (uintptr_t)NtCurrentTeb(); +} + +// We use assembly for a fast thread id on the main platforms. The TLS layout depends on +// both the OS and libc implementation so we use specific tests for each main platform. +// If you test on another platform and it works please send a PR :-) +// see also https://akkadia.org/drepper/tls.pdf for more info on the TLS register. +#elif defined(__GNUC__) && ( \ + (defined(__GLIBC__) && (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__))) \ + || (defined(__APPLE__) && (defined(__x86_64__) || defined(__aarch64__))) \ + || (defined(__BIONIC__) && (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__))) \ + || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__) || defined(__aarch64__))) \ + || (defined(__OpenBSD__) && (defined(__x86_64__) || defined(__i386__) || defined(__aarch64__))) \ + ) + +static inline void* mi_prim_tls_slot(size_t slot) mi_attr_noexcept { + void* res; + const size_t ofs = (slot*sizeof(void*)); + #if defined(__i386__) + __asm__("movl %%gs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // x86 32-bit always uses GS + #elif defined(__APPLE__) && defined(__x86_64__) + __asm__("movq %%gs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // x86_64 macOSX uses GS + #elif defined(__x86_64__) && (MI_INTPTR_SIZE==4) + __asm__("movl %%fs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // x32 ABI + #elif defined(__x86_64__) + __asm__("movq %%fs:%1, %0" : "=r" (res) : "m" (*((void**)ofs)) : ); // x86_64 Linux, BSD uses FS + #elif defined(__arm__) + void** tcb; MI_UNUSED(ofs); + __asm__ volatile ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tcb)); + res = tcb[slot]; + #elif defined(__aarch64__) + void** tcb; MI_UNUSED(ofs); + #if defined(__APPLE__) // M1, issue #343 + __asm__ volatile ("mrs %0, tpidrro_el0\nbic %0, %0, #7" : "=r" (tcb)); + #else + __asm__ volatile ("mrs %0, tpidr_el0" : "=r" (tcb)); + #endif + res = tcb[slot]; + #endif + return res; +} + +// setting a tls slot is only used on macOS for now +static inline void mi_prim_tls_slot_set(size_t slot, void* value) mi_attr_noexcept { + const size_t ofs = (slot*sizeof(void*)); + #if defined(__i386__) + __asm__("movl %1,%%gs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); // 32-bit always uses GS + #elif defined(__APPLE__) && defined(__x86_64__) + __asm__("movq %1,%%gs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); // x86_64 macOS uses GS + #elif defined(__x86_64__) && (MI_INTPTR_SIZE==4) + __asm__("movl %1,%%fs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); // x32 ABI + #elif defined(__x86_64__) + __asm__("movq %1,%%fs:%0" : "=m" (*((void**)ofs)) : "rn" (value) : ); // x86_64 Linux, BSD uses FS + #elif defined(__arm__) + void** tcb; MI_UNUSED(ofs); + __asm__ volatile ("mrc p15, 0, %0, c13, c0, 3\nbic %0, %0, #3" : "=r" (tcb)); + tcb[slot] = value; + #elif defined(__aarch64__) + void** tcb; MI_UNUSED(ofs); + #if defined(__APPLE__) // M1, issue #343 + __asm__ volatile ("mrs %0, tpidrro_el0\nbic %0, %0, #7" : "=r" (tcb)); + #else + __asm__ volatile ("mrs %0, tpidr_el0" : "=r" (tcb)); + #endif + tcb[slot] = value; + #endif +} + +static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { + #if defined(__BIONIC__) + // issue #384, #495: on the Bionic libc (Android), slot 1 is the thread id + // see: https://github.com/aosp-mirror/platform_bionic/blob/c44b1d0676ded732df4b3b21c5f798eacae93228/libc/platform/bionic/tls_defines.h#L86 + return (uintptr_t)mi_prim_tls_slot(1); + #else + // in all our other targets, slot 0 is the thread id + // glibc: https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=sysdeps/x86_64/nptl/tls.h + // apple: https://github.com/apple/darwin-xnu/blob/main/libsyscall/os/tsd.h#L36 + return (uintptr_t)mi_prim_tls_slot(0); + #endif +} + +#else + +// otherwise use portable C, taking the address of a thread local variable (this is still very fast on most platforms). +static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { + return (uintptr_t)&_mi_heap_default; +} + +#endif + + + +/* ---------------------------------------------------------------------------------------- +The thread local default heap: `_mi_prim_get_default_heap()` +This is inlined here as it is on the fast path for allocation functions. + +On most platforms (Windows, Linux, FreeBSD, NetBSD, etc), this just returns a +__thread local variable (`_mi_heap_default`). With the initial-exec TLS model this ensures +that the storage will always be available (allocated on the thread stacks). + +On some platforms though we cannot use that when overriding `malloc` since the underlying +TLS implementation (or the loader) will call itself `malloc` on a first access and recurse. +We try to circumvent this in an efficient way: +- macOSX : we use an unused TLS slot from the OS allocated slots (MI_TLS_SLOT). On OSX, the + loader itself calls `malloc` even before the modules are initialized. +- OpenBSD: we use an unused slot from the pthread block (MI_TLS_PTHREAD_SLOT_OFS). +- DragonFly: defaults are working but seem slow compared to freeBSD (see PR #323) +------------------------------------------------------------------------------------------- */ + +static inline mi_heap_t* mi_prim_get_default_heap(void); + +#if defined(MI_MALLOC_OVERRIDE) +#if defined(__APPLE__) // macOS + #define MI_TLS_SLOT 89 // seems unused? + // #define MI_TLS_RECURSE_GUARD 1 + // other possible unused ones are 9, 29, __PTK_FRAMEWORK_JAVASCRIPTCORE_KEY4 (94), __PTK_FRAMEWORK_GC_KEY9 (112) and __PTK_FRAMEWORK_OLDGC_KEY9 (89) + // see +#elif defined(__OpenBSD__) + // use end bytes of a name; goes wrong if anyone uses names > 23 characters (ptrhread specifies 16) + // see + #define MI_TLS_PTHREAD_SLOT_OFS (6*sizeof(int) + 4*sizeof(void*) + 24) + // #elif defined(__DragonFly__) + // #warning "mimalloc is not working correctly on DragonFly yet." + // #define MI_TLS_PTHREAD_SLOT_OFS (4 + 1*sizeof(void*)) // offset `uniqueid` (also used by gdb?) +#elif defined(__ANDROID__) + // See issue #381 + #define MI_TLS_PTHREAD +#endif +#endif + + +#if defined(MI_TLS_SLOT) + +static inline mi_heap_t* mi_prim_get_default_heap(void) { + mi_heap_t* heap = (mi_heap_t*)mi_prim_tls_slot(MI_TLS_SLOT); + if mi_unlikely(heap == NULL) { + #ifdef __GNUC__ + __asm(""); // prevent conditional load of the address of _mi_heap_empty + #endif + heap = (mi_heap_t*)&_mi_heap_empty; + } + return heap; +} + +#elif defined(MI_TLS_PTHREAD_SLOT_OFS) + +static inline mi_heap_t** mi_prim_tls_pthread_heap_slot(void) { + pthread_t self = pthread_self(); + #if defined(__DragonFly__) + if (self==NULL) return NULL; + #endif + return (mi_heap_t**)((uint8_t*)self + MI_TLS_PTHREAD_SLOT_OFS); +} + +static inline mi_heap_t* mi_prim_get_default_heap(void) { + mi_heap_t** pheap = mi_prim_tls_pthread_heap_slot(); + if mi_unlikely(pheap == NULL) return _mi_heap_main_get(); + mi_heap_t* heap = *pheap; + if mi_unlikely(heap == NULL) return (mi_heap_t*)&_mi_heap_empty; + return heap; +} + +#elif defined(MI_TLS_PTHREAD) + +extern pthread_key_t _mi_heap_default_key; +static inline mi_heap_t* mi_prim_get_default_heap(void) { + mi_heap_t* heap = (mi_unlikely(_mi_heap_default_key == (pthread_key_t)(-1)) ? _mi_heap_main_get() : (mi_heap_t*)pthread_getspecific(_mi_heap_default_key)); + return (mi_unlikely(heap == NULL) ? (mi_heap_t*)&_mi_heap_empty : heap); +} + +#else // default using a thread local variable; used on most platforms. + +static inline mi_heap_t* mi_prim_get_default_heap(void) { + #if defined(MI_TLS_RECURSE_GUARD) + if (mi_unlikely(!_mi_process_is_initialized)) return _mi_heap_main_get(); + #endif + return _mi_heap_default; +} + +#endif // mi_prim_get_default_heap() + + + +#endif // MIMALLOC_PRIM_H diff --git a/Source/mimalloc/include/mimalloc/track.h b/Source/mimalloc/include/mimalloc/track.h new file mode 100644 index 000000000..f78e8daa7 --- /dev/null +++ b/Source/mimalloc/include/mimalloc/track.h @@ -0,0 +1,147 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ +#pragma once +#ifndef MIMALLOC_TRACK_H +#define MIMALLOC_TRACK_H + +/* ------------------------------------------------------------------------------------------------------ +Track memory ranges with macros for tools like Valgrind address sanitizer, or other memory checkers. +These can be defined for tracking allocation: + + #define mi_track_malloc_size(p,reqsize,size,zero) + #define mi_track_free_size(p,_size) + +The macros are set up such that the size passed to `mi_track_free_size` +always matches the size of `mi_track_malloc_size`. (currently, `size == mi_usable_size(p)`). +The `reqsize` is what the user requested, and `size >= reqsize`. +The `size` is either byte precise (and `size==reqsize`) if `MI_PADDING` is enabled, +or otherwise it is the usable block size which may be larger than the original request. +Use `_mi_block_size_of(void* p)` to get the full block size that was allocated (including padding etc). +The `zero` parameter is `true` if the allocated block is zero initialized. + +Optional: + + #define mi_track_align(p,alignedp,offset,size) + #define mi_track_resize(p,oldsize,newsize) + #define mi_track_init() + +The `mi_track_align` is called right after a `mi_track_malloc` for aligned pointers in a block. +The corresponding `mi_track_free` still uses the block start pointer and original size (corresponding to the `mi_track_malloc`). +The `mi_track_resize` is currently unused but could be called on reallocations within a block. +`mi_track_init` is called at program start. + +The following macros are for tools like asan and valgrind to track whether memory is +defined, undefined, or not accessible at all: + + #define mi_track_mem_defined(p,size) + #define mi_track_mem_undefined(p,size) + #define mi_track_mem_noaccess(p,size) + +-------------------------------------------------------------------------------------------------------*/ + +#if MI_TRACK_VALGRIND +// valgrind tool + +#define MI_TRACK_ENABLED 1 +#define MI_TRACK_HEAP_DESTROY 1 // track free of individual blocks on heap_destroy +#define MI_TRACK_TOOL "valgrind" + +#include +#include + +#define mi_track_malloc_size(p,reqsize,size,zero) VALGRIND_MALLOCLIKE_BLOCK(p,size,MI_PADDING_SIZE /*red zone*/,zero) +#define mi_track_free_size(p,_size) VALGRIND_FREELIKE_BLOCK(p,MI_PADDING_SIZE /*red zone*/) +#define mi_track_resize(p,oldsize,newsize) VALGRIND_RESIZEINPLACE_BLOCK(p,oldsize,newsize,MI_PADDING_SIZE /*red zone*/) +#define mi_track_mem_defined(p,size) VALGRIND_MAKE_MEM_DEFINED(p,size) +#define mi_track_mem_undefined(p,size) VALGRIND_MAKE_MEM_UNDEFINED(p,size) +#define mi_track_mem_noaccess(p,size) VALGRIND_MAKE_MEM_NOACCESS(p,size) + +#elif MI_TRACK_ASAN +// address sanitizer + +#define MI_TRACK_ENABLED 1 +#define MI_TRACK_HEAP_DESTROY 0 +#define MI_TRACK_TOOL "asan" + +#include + +#define mi_track_malloc_size(p,reqsize,size,zero) ASAN_UNPOISON_MEMORY_REGION(p,size) +#define mi_track_free_size(p,size) ASAN_POISON_MEMORY_REGION(p,size) +#define mi_track_mem_defined(p,size) ASAN_UNPOISON_MEMORY_REGION(p,size) +#define mi_track_mem_undefined(p,size) ASAN_UNPOISON_MEMORY_REGION(p,size) +#define mi_track_mem_noaccess(p,size) ASAN_POISON_MEMORY_REGION(p,size) + +#elif MI_TRACK_ETW +// windows event tracing + +#define MI_TRACK_ENABLED 1 +#define MI_TRACK_HEAP_DESTROY 0 +#define MI_TRACK_TOOL "ETW" + +#define WIN32_LEAN_AND_MEAN +#include +#include "../src/prim/windows/etw.h" + +#define mi_track_init() EventRegistermicrosoft_windows_mimalloc(); +#define mi_track_malloc_size(p,reqsize,size,zero) EventWriteETW_MI_ALLOC((UINT64)(p), size) +#define mi_track_free_size(p,size) EventWriteETW_MI_FREE((UINT64)(p), size) + +#else +// no tracking + +#define MI_TRACK_ENABLED 0 +#define MI_TRACK_HEAP_DESTROY 0 +#define MI_TRACK_TOOL "none" + +#define mi_track_malloc_size(p,reqsize,size,zero) +#define mi_track_free_size(p,_size) + +#endif + +// ------------------- +// Utility definitions + +#ifndef mi_track_resize +#define mi_track_resize(p,oldsize,newsize) mi_track_free_size(p,oldsize); mi_track_malloc(p,newsize,false) +#endif + +#ifndef mi_track_align +#define mi_track_align(p,alignedp,offset,size) mi_track_mem_noaccess(p,offset) +#endif + +#ifndef mi_track_init +#define mi_track_init() +#endif + +#ifndef mi_track_mem_defined +#define mi_track_mem_defined(p,size) +#endif + +#ifndef mi_track_mem_undefined +#define mi_track_mem_undefined(p,size) +#endif + +#ifndef mi_track_mem_noaccess +#define mi_track_mem_noaccess(p,size) +#endif + + +#if MI_PADDING +#define mi_track_malloc(p,reqsize,zero) \ + if ((p)!=NULL) { \ + mi_assert_internal(mi_usable_size(p)==(reqsize)); \ + mi_track_malloc_size(p,reqsize,reqsize,zero); \ + } +#else +#define mi_track_malloc(p,reqsize,zero) \ + if ((p)!=NULL) { \ + mi_assert_internal(mi_usable_size(p)>=(reqsize)); \ + mi_track_malloc_size(p,reqsize,mi_usable_size(p),zero); \ + } +#endif + +#endif diff --git a/Source/mimalloc/include/mimalloc-types.h b/Source/mimalloc/include/mimalloc/types.h similarity index 61% rename from Source/mimalloc/include/mimalloc-types.h rename to Source/mimalloc/include/mimalloc/types.h index caf161d63..c7ddaaaef 100644 --- a/Source/mimalloc/include/mimalloc-types.h +++ b/Source/mimalloc/include/mimalloc/types.h @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2023, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -8,16 +8,27 @@ terms of the MIT license. A copy of the license can be found in the file #ifndef MIMALLOC_TYPES_H #define MIMALLOC_TYPES_H +// -------------------------------------------------------------------------- +// This file contains the main type definitions for mimalloc: +// mi_heap_t : all data for a thread-local heap, contains +// lists of all managed heap pages. +// mi_segment_t : a larger chunk of memory (32GiB) from where pages +// are allocated. +// mi_page_t : a mimalloc page (usually 64KiB or 512KiB) from +// where objects are allocated. +// -------------------------------------------------------------------------- + + #include // ptrdiff_t #include // uintptr_t, uint16_t, etc -#include "mimalloc-atomic.h" // _Atomic +#include "mimalloc/atomic.h" // _Atomic #ifdef _MSC_VER #pragma warning(disable:4214) // bitfield is not int -#endif +#endif // Minimal alignment necessary. On most platforms 16 bytes are needed -// due to SSE registers for example. This must be at least `MI_INTPTR_SIZE` +// due to SSE registers for example. This must be at least `sizeof(void*)` #ifndef MI_MAX_ALIGN_SIZE #define MI_MAX_ALIGN_SIZE 16 // sizeof(max_align_t) #endif @@ -29,6 +40,11 @@ terms of the MIT license. A copy of the license can be found in the file // Define NDEBUG in the release version to disable assertions. // #define NDEBUG +// Define MI_TRACK_ to enable tracking support +// #define MI_TRACK_VALGRIND 1 +// #define MI_TRACK_ASAN 1 +// #define MI_TRACK_ETW 1 + // Define MI_STAT as 1 to maintain statistics; set it to 2 to have detailed statistics (but costs some performance). // #define MI_STAT 1 @@ -55,18 +71,31 @@ terms of the MIT license. A copy of the license can be found in the file #endif // Reserve extra padding at the end of each block to be more resilient against heap block overflows. -// The padding can detect byte-precise buffer overflow on free. -#if !defined(MI_PADDING) && (MI_DEBUG>=1) +// The padding can detect buffer overflow on free. +#if !defined(MI_PADDING) && (MI_SECURE>=3 || MI_DEBUG>=1 || (MI_TRACK_VALGRIND || MI_TRACK_ASAN || MI_TRACK_ETW)) #define MI_PADDING 1 #endif +// Check padding bytes; allows byte-precise buffer overflow detection +#if !defined(MI_PADDING_CHECK) && MI_PADDING && (MI_SECURE>=3 || MI_DEBUG>=1) +#define MI_PADDING_CHECK 1 +#endif + // Encoded free lists allow detection of corrupted free lists // and can detect buffer overflows, modify after free, and double `free`s. -#if (MI_SECURE>=3 || MI_DEBUG>=1 || MI_PADDING > 0) +#if (MI_SECURE>=3 || MI_DEBUG>=1) #define MI_ENCODE_FREELIST 1 #endif + +// We used to abandon huge pages but to eagerly deallocate if freed from another thread, +// but that makes it not possible to visit them during a heap walk or include them in a +// `mi_heap_destroy`. We therefore instead reset/decommit the huge blocks if freed from +// another thread so most memory is available until it gets properly freed by the owning thread. +// #define MI_HUGE_PAGE_ABANDON 1 + + // ------------------------------------------------------ // Platform specific values // ------------------------------------------------------ @@ -83,20 +112,43 @@ terms of the MIT license. A copy of the license can be found in the file // or otherwise one might define an intptr_t type that is larger than a pointer... // ------------------------------------------------------ -#if INTPTR_MAX == 9223372036854775807LL +#if INTPTR_MAX > INT64_MAX +# define MI_INTPTR_SHIFT (4) // assume 128-bit (as on arm CHERI for example) +#elif INTPTR_MAX == INT64_MAX # define MI_INTPTR_SHIFT (3) -#elif INTPTR_MAX == 2147483647LL +#elif INTPTR_MAX == INT32_MAX # define MI_INTPTR_SHIFT (2) #else -#error platform must be 32 or 64 bits +#error platform pointers must be 32, 64, or 128 bits +#endif + +#if SIZE_MAX == UINT64_MAX +# define MI_SIZE_SHIFT (3) +typedef int64_t mi_ssize_t; +#elif SIZE_MAX == UINT32_MAX +# define MI_SIZE_SHIFT (2) +typedef int32_t mi_ssize_t; +#else +#error platform objects must be 32 or 64 bits +#endif + +#if (SIZE_MAX/2) > LONG_MAX +# define MI_ZU(x) x##ULL +# define MI_ZI(x) x##LL +#else +# define MI_ZU(x) x##UL +# define MI_ZI(x) x##L #endif #define MI_INTPTR_SIZE (1< 4 +#define MI_SEGMENT_SHIFT ( 9 + MI_SEGMENT_SLICE_SHIFT) // 32MiB +#else +#define MI_SEGMENT_SHIFT ( 7 + MI_SEGMENT_SLICE_SHIFT) // 4MiB on 32-bit +#endif + +#define MI_SMALL_PAGE_SHIFT (MI_SEGMENT_SLICE_SHIFT) // 64KiB +#define MI_MEDIUM_PAGE_SHIFT ( 3 + MI_SMALL_PAGE_SHIFT) // 512KiB + // Derived constants -#define MI_SEGMENT_SIZE (1UL<= 655360) -#error "define more bins" +#if (MI_MEDIUM_OBJ_WSIZE_MAX >= 655360) +#error "mimalloc internal: define more bins" #endif +// Maximum slice offset (15) +#define MI_MAX_SLICE_OFFSET ((MI_ALIGNMENT_MAX / MI_SEGMENT_SLICE_SIZE) - 1) + // Used as a special value to encode block sizes in 32 bits. -#define MI_HUGE_BLOCK_SIZE ((uint32_t)MI_HUGE_OBJ_SIZE_MAX) +#define MI_HUGE_BLOCK_SIZE ((uint32_t)(2*MI_GiB)) + +// blocks up to this size are always allocated aligned +#define MI_MAX_ALIGN_GUARANTEE (8*MI_MAX_ALIGN_SIZE) + +// Alignments over MI_ALIGNMENT_MAX are allocated in dedicated huge page segments +#define MI_ALIGNMENT_MAX (MI_SEGMENT_SIZE >> 1) + + +// ------------------------------------------------------ +// Mimalloc pages contain allocated blocks +// ------------------------------------------------------ // The free lists use encoded next fields // (Only actually encodes when MI_ENCODED_FREELIST is defined.) -typedef uintptr_t mi_encoded_t; +typedef uintptr_t mi_encoded_t; + +// thread id's +typedef size_t mi_threadid_t; // free lists contain blocks typedef struct mi_block_s { @@ -201,88 +273,136 @@ typedef uintptr_t mi_thread_free_t; // We don't count `freed` (as |free|) but use `used` to reduce // the number of memory accesses in the `mi_page_all_free` function(s). // -// Notes: +// Notes: // - Access is optimized for `mi_free` and `mi_page_alloc` (in `alloc.c`) // - Using `uint16_t` does not seem to slow things down // - The size is 8 words on 64-bit which helps the page index calculations -// (and 10 words on 32-bit, and encoded free lists add 2 words. Sizes 10 +// (and 10 words on 32-bit, and encoded free lists add 2 words. Sizes 10 // and 12 are still good for address calculation) -// - To limit the structure size, the `xblock_size` is 32-bits only; for +// - To limit the structure size, the `xblock_size` is 32-bits only; for // blocks > MI_HUGE_BLOCK_SIZE the size is determined from the segment page size // - `thread_free` uses the bottom bits as a delayed-free flags to optimize // concurrent frees where only the first concurrent free adds to the owning // heap `thread_delayed_free` list (see `alloc.c:mi_free_block_mt`). // The invariant is that no-delayed-free is only set if there is -// at least one block that will be added, or as already been added, to +// at least one block that will be added, or as already been added, to // the owning heap `thread_delayed_free` list. This guarantees that pages // will be freed correctly even if only other threads free blocks. typedef struct mi_page_s { // "owned" by the segment - uint8_t segment_idx; // index in the segment `pages` array, `page == &segment->pages[page->segment_idx]` - uint8_t segment_in_use:1; // `true` if the segment allocated this page - uint8_t is_reset:1; // `true` if the page memory was reset - uint8_t is_committed:1; // `true` if the page virtual memory is committed - uint8_t is_zero_init:1; // `true` if the page was zero initialized + uint32_t slice_count; // slices in this page (0 if not a page) + uint32_t slice_offset; // distance from the actual page data slice (0 if a page) + uint8_t is_reset : 1; // `true` if the page memory was reset + uint8_t is_committed : 1; // `true` if the page virtual memory is committed + uint8_t is_zero_init : 1; // `true` if the page was zero initialized // layout like this to optimize access in `mi_malloc` and `mi_free` uint16_t capacity; // number of blocks committed, must be the first field, see `segment.c:page_clear` uint16_t reserved; // number of blocks reserved in memory mi_page_flags_t flags; // `in_full` and `has_aligned` flags (8 bits) - uint8_t is_zero:1; // `true` if the blocks in the free list are zero initialized - uint8_t retire_expire:7; // expiration count for retired blocks + uint8_t is_zero : 1; // `true` if the blocks in the free list are zero initialized + uint8_t retire_expire : 7; // expiration count for retired blocks mi_block_t* free; // list of available free blocks (`malloc` allocates from this list) - #ifdef MI_ENCODE_FREELIST - uintptr_t keys[2]; // two random keys to encode the free lists (see `_mi_block_next`) - #endif uint32_t used; // number of blocks in use (including blocks in `local_free` and `thread_free`) - uint32_t xblock_size; // size available in each block (always `>0`) - + uint32_t xblock_size; // size available in each block (always `>0`) mi_block_t* local_free; // list of deferred free blocks by this thread (migrates to `free`) + + #if (MI_ENCODE_FREELIST || MI_PADDING) + uintptr_t keys[2]; // two random keys to encode the free lists (see `_mi_block_next`) or padding canary + #endif + _Atomic(mi_thread_free_t) xthread_free; // list of deferred free blocks freed by other threads _Atomic(uintptr_t) xheap; - + struct mi_page_s* next; // next page owned by this thread with the same `block_size` struct mi_page_s* prev; // previous page owned by this thread with the same `block_size` + + // 64-bit 9 words, 32-bit 12 words, (+2 for secure) + #if MI_INTPTR_SIZE==8 + uintptr_t padding[1]; + #endif } mi_page_t; typedef enum mi_page_kind_e { - MI_PAGE_SMALL, // small blocks go into 64kb pages inside a segment - MI_PAGE_MEDIUM, // medium blocks go into 512kb pages inside a segment - MI_PAGE_LARGE, // larger blocks go into a single page spanning a whole segment - MI_PAGE_HUGE // huge blocks (>512kb) are put into a single page in a segment of the exact size (but still 2mb aligned) + MI_PAGE_SMALL, // small blocks go into 64KiB pages inside a segment + MI_PAGE_MEDIUM, // medium blocks go into medium pages inside a segment + MI_PAGE_LARGE, // larger blocks go into a page of just one block + MI_PAGE_HUGE, // huge blocks (> 16 MiB) are put into a single page in a single segment. } mi_page_kind_t; -// Segments are large allocated memory blocks (2mb on 64 bit) from +typedef enum mi_segment_kind_e { + MI_SEGMENT_NORMAL, // MI_SEGMENT_SIZE size with pages inside. + MI_SEGMENT_HUGE, // > MI_LARGE_SIZE_MAX segment with just one huge page inside. +} mi_segment_kind_t; + +// ------------------------------------------------------ +// A segment holds a commit mask where a bit is set if +// the corresponding MI_COMMIT_SIZE area is committed. +// The MI_COMMIT_SIZE must be a multiple of the slice +// size. If it is equal we have the most fine grained +// decommit (but setting it higher can be more efficient). +// The MI_MINIMAL_COMMIT_SIZE is the minimal amount that will +// be committed in one go which can be set higher than +// MI_COMMIT_SIZE for efficiency (while the decommit mask +// is still tracked in fine-grained MI_COMMIT_SIZE chunks) +// ------------------------------------------------------ + +#define MI_MINIMAL_COMMIT_SIZE (16*MI_SEGMENT_SLICE_SIZE) // 1MiB +#define MI_COMMIT_SIZE (MI_SEGMENT_SLICE_SIZE) // 64KiB +#define MI_COMMIT_MASK_BITS (MI_SEGMENT_SIZE / MI_COMMIT_SIZE) +#define MI_COMMIT_MASK_FIELD_BITS MI_SIZE_BITS +#define MI_COMMIT_MASK_FIELD_COUNT (MI_COMMIT_MASK_BITS / MI_COMMIT_MASK_FIELD_BITS) + +#if (MI_COMMIT_MASK_BITS != (MI_COMMIT_MASK_FIELD_COUNT * MI_COMMIT_MASK_FIELD_BITS)) +#error "the segment size must be exactly divisible by the (commit size * size_t bits)" +#endif + +typedef struct mi_commit_mask_s { + size_t mask[MI_COMMIT_MASK_FIELD_COUNT]; +} mi_commit_mask_t; + +typedef mi_page_t mi_slice_t; +typedef int64_t mi_msecs_t; + + +// Segments are large allocated memory blocks (8mb on 64 bit) from // the OS. Inside segments we allocated fixed size _pages_ that // contain blocks. typedef struct mi_segment_s { - // memory fields - size_t memid; // id for the os-level memory manager - bool mem_is_pinned; // `true` if we cannot decommit/reset/protect in this memory (i.e. when allocated using large OS pages) - bool mem_is_committed; // `true` if the whole segment is eagerly committed + size_t memid; // memory id for arena allocation + bool mem_is_pinned; // `true` if we cannot decommit/reset/protect in this memory (i.e. when allocated using large OS pages) + bool mem_is_large; // in large/huge os pages? + bool mem_is_committed; // `true` if the whole segment is eagerly committed + size_t mem_alignment; // page alignment for huge pages (only used for alignment > MI_ALIGNMENT_MAX) + size_t mem_align_offset; // offset for huge page alignment (only used for alignment > MI_ALIGNMENT_MAX) + + bool allow_decommit; + mi_msecs_t decommit_expire; + mi_commit_mask_t decommit_mask; + mi_commit_mask_t commit_mask; - // segment fields _Atomic(struct mi_segment_s*) abandoned_next; - struct mi_segment_s* next; // must be the first segment field after abandoned_next -- see `segment.c:segment_init` - struct mi_segment_s* prev; - size_t abandoned; // abandoned pages (i.e. the original owning thread stopped) (`abandoned <= used`) - size_t abandoned_visits; // count how often this segment is visited in the abandoned list (to force reclaim if it is too long) + // from here is zero initialized + struct mi_segment_s* next; // the list of freed segments in the cache (must be first field, see `segment.c:mi_segment_init`) + + size_t abandoned; // abandoned pages (i.e. the original owning thread stopped) (`abandoned <= used`) + size_t abandoned_visits; // count how often this segment is visited in the abandoned list (to force reclaim it it is too long) + size_t used; // count of pages in use + uintptr_t cookie; // verify addresses in debug mode: `mi_ptr_cookie(segment) == segment->cookie` - size_t used; // count of pages in use (`used <= capacity`) - size_t capacity; // count of available pages (`#free + used`) - size_t segment_size; // for huge pages this may be different from `MI_SEGMENT_SIZE` - size_t segment_info_size;// space we are using from the first page for segment meta-data and possible guard pages. - uintptr_t cookie; // verify addresses in secure mode: `_mi_ptr_cookie(segment) == segment->cookie` + size_t segment_slices; // for huge segments this may be different from `MI_SLICES_PER_SEGMENT` + size_t segment_info_slices; // initial slices we are using segment info and possible guard pages. // layout like this to optimize access in `mi_free` - size_t page_shift; // `1 << page_shift` == the page sizes == `page->block_size * page->reserved` (unless the first page, then `-segment_info_size`). - _Atomic(uintptr_t) thread_id; // unique id of the thread owning this segment - mi_page_kind_t page_kind; // kind of pages: small, large, or huge - mi_page_t pages[1]; // up to `MI_SMALL_PAGES_PER_SEGMENT` pages + mi_segment_kind_t kind; + size_t slice_entries; // entries in the `slices` array, at most `MI_SLICES_PER_SEGMENT` + _Atomic(mi_threadid_t) thread_id; // unique id of the thread owning this segment + + mi_slice_t slices[MI_SLICES_PER_SEGMENT+1]; // one more for huge blocks with large alignment } mi_segment_t; @@ -316,10 +436,11 @@ typedef struct mi_random_cxt_s { uint32_t input[16]; uint32_t output[16]; int output_available; + bool weak; } mi_random_ctx_t; -// In debug mode there is a padding stucture at the end of the blocks to check for buffer overflows +// In debug mode there is a padding structure at the end of the blocks to check for buffer overflows #if (MI_PADDING) typedef struct mi_padding_s { uint32_t canary; // encoded block value to check validity of the padding (in case of overflow) @@ -341,7 +462,8 @@ struct mi_heap_s { mi_page_t* pages_free_direct[MI_PAGES_DIRECT]; // optimize: array where every entry points a page with possibly free blocks in the corresponding queue for that size. mi_page_queue_t pages[MI_BIN_FULL + 1]; // queue of pages for each size class (or "bin") _Atomic(mi_block_t*) thread_delayed_free; - uintptr_t thread_id; // thread this heap belongs too + mi_threadid_t thread_id; // thread this heap belongs too + mi_arena_id_t arena_id; // arena id if the heap belongs to a specific arena (or 0) uintptr_t cookie; // random cookie to verify pointers (see `_mi_ptr_cookie`) uintptr_t keys[2]; // two random keys used to encode the `thread_delayed_free` list mi_random_ctx_t random; // random number context used for secure allocation @@ -358,9 +480,15 @@ struct mi_heap_s { // Debug // ------------------------------------------------------ +#if !defined(MI_DEBUG_UNINIT) #define MI_DEBUG_UNINIT (0xD0) +#endif +#if !defined(MI_DEBUG_FREED) #define MI_DEBUG_FREED (0xDF) +#endif +#if !defined(MI_DEBUG_PADDING) #define MI_DEBUG_PADDING (0xDE) +#endif #if (MI_DEBUG) // use our own assertion to print without memory allocation @@ -418,7 +546,7 @@ typedef struct mi_stats_s { mi_stat_count_t threads; mi_stat_count_t normal; mi_stat_count_t huge; - mi_stat_count_t giant; + mi_stat_count_t large; mi_stat_count_t malloc; mi_stat_count_t segments_cache; mi_stat_counter_t pages_extended; @@ -428,7 +556,7 @@ typedef struct mi_stats_s { mi_stat_counter_t searches; mi_stat_counter_t normal_count; mi_stat_counter_t huge_count; - mi_stat_counter_t giant_count; + mi_stat_counter_t large_count; #if MI_STAT>1 mi_stat_count_t normal_bins[MI_BIN_HUGE+1]; #endif @@ -457,13 +585,15 @@ void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount); // Thread Local data // ------------------------------------------------------ -typedef int64_t mi_msecs_t; +// A "span" is is an available range of slices. The span queues keep +// track of slice spans of at most the given `slice_count` (but more than the previous size class). +typedef struct mi_span_queue_s { + mi_slice_t* first; + mi_slice_t* last; + size_t slice_count; +} mi_span_queue_t; -// Queue of segments -typedef struct mi_segment_queue_s { - mi_segment_t* first; - mi_segment_t* last; -} mi_segment_queue_t; +#define MI_SEGMENT_BIN_MAX (35) // 35 == mi_segment_bin(MI_SLICES_PER_SEGMENT) // OS thread local data typedef struct mi_os_tld_s { @@ -471,18 +601,14 @@ typedef struct mi_os_tld_s { mi_stats_t* stats; // points to tld stats } mi_os_tld_t; + // Segments thread local data typedef struct mi_segments_tld_s { - mi_segment_queue_t small_free; // queue of segments with free small pages - mi_segment_queue_t medium_free; // queue of segments with free medium pages - mi_page_queue_t pages_reset; // queue of freed pages that can be reset + mi_span_queue_t spans[MI_SEGMENT_BIN_MAX+1]; // free slice spans inside segments size_t count; // current number of segments; size_t peak_count; // peak number of segments size_t current_size; // current size of all segments size_t peak_size; // peak size of all segments - size_t cache_count; // number of segments in the cache - size_t cache_size; // total size of all segments in the cache - mi_segment_t* cache; // (small) cache of segments mi_stats_t* stats; // points to tld stats mi_os_tld_t* os; // points to os stats } mi_segments_tld_t; diff --git a/Source/mimalloc/mimalloc.pc.in b/Source/mimalloc/mimalloc.pc.in new file mode 100644 index 000000000..36da20388 --- /dev/null +++ b/Source/mimalloc/mimalloc.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@libdir_for_pc_file@ +includedir=@includedir_for_pc_file@ + +Name: @PROJECT_NAME@ +Description: A compact general purpose allocator with excellent performance +Version: @PACKAGE_VERSION@ +URL: https://github.com/microsoft/mimalloc/ +Libs: -L${libdir} -lmimalloc +Libs.private: @pc_libraries@ +Cflags: -I${includedir} diff --git a/Source/mimalloc/readme.md b/Source/mimalloc/readme.md index e36623680..003cd8cf7 100644 --- a/Source/mimalloc/readme.md +++ b/Source/mimalloc/readme.md @@ -9,24 +9,26 @@ mimalloc (pronounced "me-malloc") is a general purpose allocator with excellent [performance](#performance) characteristics. -Initially developed by Daan Leijen for the run-time systems of the +Initially developed by Daan Leijen for the runtime systems of the [Koka](https://koka-lang.github.io) and [Lean](https://github.com/leanprover/lean) languages. -Latest release tag: `v2.0.1` (beta, 2021-04-06). -Latest stable tag: `v1.7.1` (2021-04-06). +Latest release tag: `v2.1.1` (2023-04-03). +Latest stable tag: `v1.8.1` (2023-04-03). mimalloc is a drop-in replacement for `malloc` and can be used in other programs without code changes, for example, on dynamically linked ELF-based systems (Linux, BSD, etc.) you can use it as: ``` -> LD_PRELOAD=/usr/bin/libmimalloc.so myprogram +> LD_PRELOAD=/usr/lib/libmimalloc.so myprogram ``` -It also has an easy way to override the default allocator in [Windows](#override_on_windows). Notable aspects of the design include: +It also includes a robust way to override the default allocator in [Windows](#override_on_windows). Notable aspects of the design include: - __small and consistent__: the library is about 8k LOC using simple and consistent data structures. This makes it very suitable to integrate and adapt in other projects. For runtime systems it provides hooks for a monotonic _heartbeat_ and deferred freeing (for bounded worst-case times with reference counting). + Partly due to its simplicity, mimalloc has been ported to many systems (Windows, macOS, + Linux, WASM, various BSD's, Haiku, MUSL, etc) and has excellent support for dynamic overriding. - __free list sharding__: instead of one big free list (per size class) we have many smaller lists per "mimalloc page" which reduces fragmentation and increases locality -- @@ -36,13 +38,13 @@ It also has an easy way to override the default allocator in [Windows](#override per mimalloc page, but for each page we have multiple free lists. In particular, there is one list for thread-local `free` operations, and another one for concurrent `free` operations. Free-ing from another thread can now be a single CAS without needing - sophisticated coordination between threads. Since there will be + sophisticated coordination between threads. Since there will be thousands of separate free lists, contention is naturally distributed over the heap, and the chance of contending on a single location will be low -- this is quite similar to randomized algorithms like skip lists where adding a random oracle removes the need for a more complex algorithm. - __eager page reset__: when a "page" becomes empty (with increased chance - due to free list sharding) the memory is marked to the OS as unused ("reset" or "purged") + due to free list sharding) the memory is marked to the OS as unused (reset or decommitted) reducing (real) memory pressure and fragmentation, especially in long running programs. - __secure__: _mimalloc_ can be built in secure mode, adding guard pages, @@ -50,66 +52,74 @@ It also has an easy way to override the default allocator in [Windows](#override heap vulnerabilities. The performance penalty is usually around 10% on average over our benchmarks. - __first-class heaps__: efficiently create and use multiple heaps to allocate across different regions. - A heap can be destroyed at once instead of deallocating each object separately. + A heap can be destroyed at once instead of deallocating each object separately. - __bounded__: it does not suffer from _blowup_ \[1\], has bounded worst-case allocation - times (_wcat_), bounded space overhead (~0.2% meta-data, with at most 12.5% waste in allocation sizes), - and has no internal points of contention using only atomic operations. + times (_wcat_) (upto OS primitives), bounded space overhead (~0.2% meta-data, with low + internal fragmentation), and has no internal points of contention using only atomic operations. - __fast__: In our benchmarks (see [below](#performance)), _mimalloc_ outperforms other leading allocators (_jemalloc_, _tcmalloc_, _Hoard_, etc), - and often uses less memory. A nice property - is that it does consistently well over a wide range of benchmarks. There is also good huge OS page - support for larger server programs. + and often uses less memory. A nice property is that it does consistently well over a wide range + of benchmarks. There is also good huge OS page support for larger server programs. The [documentation](https://microsoft.github.io/mimalloc) gives a full overview of the API. -You can read more on the design of _mimalloc_ in the [technical report](https://www.microsoft.com/en-us/research/publication/mimalloc-free-list-sharding-in-action) which also has detailed benchmark results. +You can read more on the design of _mimalloc_ in the [technical report](https://www.microsoft.com/en-us/research/publication/mimalloc-free-list-sharding-in-action) which also has detailed benchmark results. -Enjoy! +Enjoy! ### Branches -* `master`: latest stable release. -* `dev`: development branch for mimalloc v1. -* `dev-slice`: development branch for mimalloc v2 with a new algorithm for managing internal mimalloc pages. +* `master`: latest stable release (based on `dev-slice`). +* `dev`: development branch for mimalloc v1. Use this branch for submitting PR's. +* `dev-slice`: development branch for mimalloc v2. This branch is downstream of `dev`. ### Releases -* 2021-04-06, `v1.7.1`, `v2.0.1` (beta): fix bug in arena allocation for huge pages, improved aslr on large allocations, improved M1 support (still experimental). - -* 2021-01-31, `v2.0.0`: beta release 2.0: new algorithm for managing internal mimalloc pages that tends to use reduce memory usage - and fragmentation compared to mimalloc v1 (especially for large workloads). Should otherwise have similar performance +Note: the `v2.x` version has a new algorithm for managing internal mimalloc pages that tends to use reduce memory usage + and fragmentation compared to mimalloc `v1.x` (especially for large workloads). Should otherwise have similar performance (see [below](#performance)); please report if you observe any significant performance regression. +* 2023-04-03, `v1.8.1`, `v2.1.1`: Fixes build issues on some platforms. + +* 2023-03-29, `v1.8.0`, `v2.1.0`: Improved support dynamic overriding on Windows 11. Improved tracing precision + with [asan](#asan) and [Valgrind](#valgrind), and added Windows event tracing [ETW](#ETW) (contributed by Xinglong He). Created an OS + abstraction layer to make it easier to port and separate platform dependent code (in `src/prim`). Fixed C++ STL compilation on older Microsoft C++ compilers, and various small bug fixes. + +* 2022-12-23, `v1.7.9`, `v2.0.9`: Supports building with [asan](#asan) and improved [Valgrind](#valgrind) support. + Support abitrary large alignments (in particular for `std::pmr` pools). + Added C++ STL allocators attached to a specific heap (thanks @vmarkovtsev). + Heap walks now visit all object (including huge objects). Support Windows nano server containers (by Johannes Schindelin,@dscho). + Various small bug fixes. + +* 2022-11-03, `v1.7.7`, `v2.0.7`: Initial support for [Valgrind](#valgrind) for leak testing and heap block overflow + detection. Initial + support for attaching heaps to a speficic memory area (only in v2). Fix `realloc` behavior for zero size blocks, remove restriction to integral multiple of the alignment in `alloc_align`, improved aligned allocation performance, reduced contention with many threads on few processors (thank you @dposluns!), vs2022 support, support `pkg-config`, . + +* 2022-04-14, `v1.7.6`, `v2.0.6`: fix fallback path for aligned OS allocation on Windows, improve Windows aligned allocation + even when compiling with older SDK's, fix dynamic overriding on macOS Monterey, fix MSVC C++ dynamic overriding, fix + warnings under Clang 14, improve performance if many OS threads are created and destroyed, fix statistics for large object + allocations, using MIMALLOC_VERBOSE=1 has no maximum on the number of error messages, various small fixes. + +* 2022-02-14, `v1.7.5`, `v2.0.5` (alpha): fix malloc override on + Windows 11, fix compilation with musl, potentially reduced + committed memory, add `bin/minject` for Windows, + improved wasm support, faster aligned allocation, + various small fixes. + +* 2021-11-14, `v1.7.3`, `v2.0.3` (beta): improved WASM support, improved macOS support and performance (including + M1), improved performance for v2 for large objects, Python integration improvements, more standard + installation directories, various small fixes. + +* 2021-06-17, `v1.7.2`, `v2.0.2` (beta): support M1, better installation layout on Linux, fix + thread_id on Android, prefer 2-6TiB area for aligned allocation to work better on pre-windows 8, various small fixes. + +* 2021-04-06, `v1.7.1`, `v2.0.1` (beta): fix bug in arena allocation for huge pages, improved aslr on large allocations, initial M1 support (still experimental). + +* 2021-01-31, `v2.0.0`: beta release 2.0: new slice algorithm for managing internal mimalloc pages. + * 2021-01-31, `v1.7.0`: stable release 1.7: support explicit user provided memory regions, more precise statistics, improve macOS overriding, initial support for Apple M1, improved DragonFly support, faster memcpy on Windows, various small fixes. -### Older Releases - -* 2020-09-24, `v1.6.7`: stable release 1.6: using standard C atomics, passing tsan testing, improved - handling of failing to commit on Windows, add [`mi_process_info`](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc.h#L156) api call. -* 2020-08-06, `v1.6.4`: stable release 1.6: improved error recovery in low-memory situations, - support for IllumOS and Haiku, NUMA support for Vista/XP, improved NUMA detection for AMD Ryzen, ubsan support. -* 2020-05-05, `v1.6.3`: stable release 1.6: improved behavior in out-of-memory situations, improved malloc zones on macOS, - build PIC static libraries by default, add option to abort on out-of-memory, line buffered statistics. -* 2020-04-20, `v1.6.2`: stable release 1.6: fix compilation on Android, MingW, Raspberry, and Conda, - stability fix for Windows 7, fix multiple mimalloc instances in one executable, fix `strnlen` overload, - fix aligned debug padding. -* 2020-02-17, `v1.6.1`: stable release 1.6: minor updates (build with clang-cl, fix alignment issue for small objects). -* 2020-02-09, `v1.6.0`: stable release 1.6: fixed potential memory leak, improved overriding - and thread local support on FreeBSD, NetBSD, DragonFly, and macOSX. New byte-precise - heap block overflow detection in debug mode (besides the double-free detection and free-list - corruption detection). Add `nodiscard` attribute to most allocation functions. - Enable `MIMALLOC_PAGE_RESET` by default. New reclamation strategy for abandoned heap pages - for better memory footprint. -* 2020-02-09, `v1.5.0`: stable release 1.5: improved free performance, small bug fixes. -* 2020-01-22, `v1.4.0`: stable release 1.4: improved performance for delayed OS page reset, -more eager concurrent free, addition of STL allocator, fixed potential memory leak. -* 2020-01-15, `v1.3.0`: stable release 1.3: bug fixes, improved randomness and [stronger -free list encoding](https://github.com/microsoft/mimalloc/blob/783e3377f79ee82af43a0793910a9f2d01ac7863/include/mimalloc-internal.h#L396) in secure mode. -* 2019-12-22, `v1.2.2`: stable release 1.2: minor updates. -* 2019-11-22, `v1.2.0`: stable release 1.2: bug fixes, improved secure mode (free list corruption checks, double free mitigation). Improved dynamic overriding on Windows. -* 2019-10-07, `v1.1.0`: stable release 1.1. -* 2019-09-01, `v1.0.8`: pre-release 8: more robust windows dynamic overriding, initial huge page support. -* 2019-08-10, `v1.0.6`: pre-release 6: various performance improvements. +* [Older release notes](#older-release-notes) Special thanks to: @@ -119,9 +129,11 @@ Special thanks to: memory model bugs using the [genMC] model checker. * Weipeng Liu (@pongba), Zhuowei Li, Junhua Wang, and Jakub Szymanski, for their early support of mimalloc and deployment at large scale services, leading to many improvements in the mimalloc algorithms for large workloads. -* Jason Gibson (@jasongibson) for exhaustive testing on large scale workloads and server environments, and finding complex bugs +* Jason Gibson (@jasongibson) for exhaustive testing on large scale workloads and server environments, and finding complex bugs in (early versions of) `mimalloc`. -* Manuel Pöter (@mpoeter) and Sam Gross (@colesbury) for finding an ABA concurrency issue in abandoned segment reclamation. +* Manuel Pöter (@mpoeter) and Sam Gross(@colesbury) for finding an ABA concurrency issue in abandoned segment reclamation. Sam also created the [no GIL](https://github.com/colesbury/nogil) Python fork which + uses mimalloc internally. + [genMC]: https://plv.mpi-sws.org/genmc/ @@ -129,15 +141,18 @@ Special thanks to: mimalloc is used in various large scale low-latency services and programs, for example: - - - + + + + + + # Building ## Windows -Open `ide/vs2019/mimalloc.sln` in Visual Studio 2019 and build (or `ide/vs2017/mimalloc.sln`). +Open `ide/vs2019/mimalloc.sln` in Visual Studio 2019 and build. The `mimalloc` project builds a static library (in `out/msvc-x64`), while the `mimalloc-override` project builds a DLL for overriding malloc in the entire program. @@ -186,6 +201,11 @@ Notes: 2. Install CCMake: `sudo apt-get install cmake-curses-gui` +## Single source + +You can also directly build the single `src/static.c` file as part of your project without +needing `cmake` at all. Make sure to also add the mimalloc `include` directory to the include path. + # Using the library @@ -297,10 +317,13 @@ or via environment variables: `MIMALLOC_EAGER_COMMIT_DELAY=N` (`N` is 1 by default) to delay the initial `N` segments (of 4MiB) of a thread to not allocate in the huge OS pages; this prevents threads that are short lived and allocate just a little to take up space in the huge OS page area (which cannot be reset). + The huge pages are usually allocated evenly among NUMA nodes. + We can use `MIMALLOC_RESERVE_HUGE_OS_PAGES_AT=N` where `N` is the numa node (starting at 0) to allocate all + the huge pages at a specific numa node instead. Use caution when using `fork` in combination with either large or huge OS pages: on a fork, the OS uses copy-on-write for all pages in the original process including the huge OS pages. When any memory is now written in that area, the -OS will copy the entire 1GiB huge page (or 2MiB large page) which can cause the memory usage to grow in big increments. +OS will copy the entire 1GiB huge page (or 2MiB large page) which can cause the memory usage to grow in large increments. [linux-huge]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/tuning_and_optimizing_red_hat_enterprise_linux_for_oracle_9i_and_10g_databases/sect-oracle_9i_and_10g_tuning_guide-large_memory_optimization_big_pages_and_huge_pages-configuring_huge_pages_in_red_hat_enterprise_linux_4_or_5 [windows-huge]: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows?view=sql-server-2017 @@ -332,15 +355,15 @@ When _mimalloc_ is built using debug mode, various checks are done at runtime to - Corrupted free-lists and some forms of use-after-free are detected. -# Overriding Malloc +# Overriding Standard Malloc -Overriding the standard `malloc` can be done either _dynamically_ or _statically_. +Overriding the standard `malloc` (and `new`) can be done either _dynamically_ or _statically_. ## Dynamic override This is the recommended way to override the standard malloc interface. -### Override on Linux, BSD +### Dynamic Override on Linux, BSD On these ELF-based systems we preload the mimalloc shared library so all calls to the standard `malloc` interface are @@ -359,21 +382,20 @@ or run with the debug version to get detailed statistics: > env MIMALLOC_SHOW_STATS=1 LD_PRELOAD=/usr/lib/libmimalloc-debug.so myprogram ``` -### Override on MacOS +### Dynamic Override on MacOS On macOS we can also preload the mimalloc shared library so all calls to the standard `malloc` interface are resolved to the _mimalloc_ library. ``` -> env DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=/usr/lib/libmimalloc.dylib myprogram +> env DYLD_INSERT_LIBRARIES=/usr/lib/libmimalloc.dylib myprogram ``` Note that certain security restrictions may apply when doing this from the [shell](https://stackoverflow.com/questions/43941322/dyld-insert-libraries-ignored-when-calling-application-through-bash). -(Note: macOS support for dynamic overriding is recent, please report any issues.) -### Override on Windows +### Dynamic Override on Windows Overriding on Windows is robust and has the particular advantage to be able to redirect all malloc/free calls that go through @@ -381,7 +403,7 @@ the (dynamic) C runtime allocator, including those from other DLL's or libraries The overriding on Windows requires that you link your program explicitly with the mimalloc DLL and use the C-runtime library as a DLL (using the `/MD` or `/MDd` switch). -Also, the `mimalloc-redirect.dll` (or `mimalloc-redirect32.dll`) must be available +Also, the `mimalloc-redirect.dll` (or `mimalloc-redirect32.dll`) must be put in the same folder as the main `mimalloc-override.dll` at runtime (as it is a dependency). The redirection DLL ensures that all calls to the C runtime malloc API get redirected to mimalloc (in `mimalloc-override.dll`). @@ -406,13 +428,13 @@ Such patching can be done for example with [CFF Explorer](https://ntcore.com/?pa On Unix-like systems, you can also statically link with _mimalloc_ to override the standard malloc interface. The recommended way is to link the final program with the -_mimalloc_ single object file (`mimalloc-override.o`). We use +_mimalloc_ single object file (`mimalloc.o`). We use an object file instead of a library file as linkers give preference to that over archives to resolve symbols. To ensure that the standard malloc interface resolves to the _mimalloc_ library, link it as the first object file. For example: ``` -> gcc -o myprogram mimalloc-override.o myfile1.c ... +> gcc -o myprogram mimalloc.o myfile1.c ... ``` Another way to override statically that works on all platforms, is to @@ -422,6 +444,96 @@ This is provided by [`mimalloc-override.h`](https://github.com/microsoft/mimallo under your control or otherwise mixing of pointers from different heaps may occur! +## Tools + +Generally, we recommend using the standard allocator with memory tracking tools, but mimalloc +can also be build to support the [address sanitizer][asan] or the excellent [Valgrind] tool. +Moreover, it can be build to support Windows event tracing ([ETW]). +This has a small performance overhead but does allow detecting memory leaks and byte-precise +buffer overflows directly on final executables. See also the `test/test-wrong.c` file to test with various tools. + +### Valgrind + +To build with [valgrind] support, use the `MI_TRACK_VALGRIND=ON` cmake option: + +``` +> cmake ../.. -DMI_TRACK_VALGRIND=ON +``` + +This can also be combined with secure mode or debug mode. +You can then run your programs directly under valgrind: + +``` +> valgrind +``` + +If you rely on overriding `malloc`/`free` by mimalloc (instead of using the `mi_malloc`/`mi_free` API directly), +you also need to tell `valgrind` to not intercept those calls itself, and use: + +``` +> MIMALLOC_SHOW_STATS=1 valgrind --soname-synonyms=somalloc=*mimalloc* -- +``` + +By setting the `MIMALLOC_SHOW_STATS` environment variable you can check that mimalloc is indeed +used and not the standard allocator. Even though the [Valgrind option][valgrind-soname] +is called `--soname-synonyms`, this also +works when overriding with a static library or object file. Unfortunately, it is not possible to +dynamically override mimalloc using `LD_PRELOAD` together with `valgrind`. +See also the `test/test-wrong.c` file to test with `valgrind`. + +Valgrind support is in its initial development -- please report any issues. + +[Valgrind]: https://valgrind.org/ +[valgrind-soname]: https://valgrind.org/docs/manual/manual-core.html#opt.soname-synonyms + +### ASAN + +To build with the address sanitizer, use the `-DMI_TRACK_ASAN=ON` cmake option: + +``` +> cmake ../.. -DMI_TRACK_ASAN=ON +``` + +This can also be combined with secure mode or debug mode. +You can then run your programs as:' + +``` +> ASAN_OPTIONS=verbosity=1 +``` + +When you link a program with an address sanitizer build of mimalloc, you should +generally compile that program too with the address sanitizer enabled. +For example, assuming you build mimalloc in `out/debug`: + +``` +clang -g -o test-wrong -Iinclude test/test-wrong.c out/debug/libmimalloc-asan-debug.a -lpthread -fsanitize=address -fsanitize-recover=address +``` + +Since the address sanitizer redirects the standard allocation functions, on some platforms (macOSX for example) +it is required to compile mimalloc with `-DMI_OVERRIDE=OFF`. +Adress sanitizer support is in its initial development -- please report any issues. + +[asan]: https://github.com/google/sanitizers/wiki/AddressSanitizer + +### ETW + +Event tracing for Windows ([ETW]) provides a high performance way to capture all allocations though +mimalloc and analyze them later. To build with ETW support, use the `-DMI_TRACE_ETW=ON` cmake option. + +You can then capture an allocation trace using the Windows performance recorder (WPR), using the +`src/prim/windows/etw-mimalloc.wprp` profile. In an admin prompt, you can use: +``` +> wpr -start src\prim\windows\etw-mimalloc.wprp -filemode +> +> wpr -stop .etl +``` +and then open `.etl` in the Windows Performance Analyzer (WPA), or +use a tool like [TraceControl] that is specialized for analyzing mimalloc traces. + +[ETW]: https://learn.microsoft.com/en-us/windows-hardware/test/wpt/event-tracing-for-windows +[TraceControl]: https://github.com/xinglonghe/TraceControl + + # Performance Last update: 2021-01-30 @@ -527,7 +639,7 @@ The _alloc-test_, by [OLogN Technologies AG](http://ithare.com/testing-memory-allocators-ptmalloc2-tcmalloc-hoard-jemalloc-while-trying-to-simulate-real-world-loads/), is a very allocation intensive benchmark doing millions of allocations in various size classes. The test is scaled such that when an allocator performs almost identically on _alloc-test1_ as _alloc-testN_ it -means that it scales linearly. +means that it scales linearly. The _sh6bench_ and _sh8bench_ benchmarks are developed by [MicroQuill](http://www.microquill.com/) as part of SmartHeap. @@ -631,6 +743,7 @@ see the differences in the _larsonN_, _mstressN_, and _xmalloc-testN_ benchmarks --> + # References - \[1] Emery D. Berger, Kathryn S. McKinley, Robert D. Blumofe, and Paul R. Wilson. @@ -668,7 +781,6 @@ see the differences in the _larsonN_, _mstressN_, and _xmalloc-testN_ benchmarks In Proceedings of the 2019 ACM SIGPLAN International Symposium on Memory Management, 122–135. ACM. 2019. --> - # Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a @@ -678,3 +790,33 @@ the rights to use your contribution. For details, visit https://cla.microsoft.co When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. + + +# Older Release Notes + +* 2020-09-24, `v1.6.7`: stable release 1.6: using standard C atomics, passing tsan testing, improved + handling of failing to commit on Windows, add [`mi_process_info`](https://github.com/microsoft/mimalloc/blob/master/include/mimalloc.h#L156) api call. +* 2020-08-06, `v1.6.4`: stable release 1.6: improved error recovery in low-memory situations, + support for IllumOS and Haiku, NUMA support for Vista/XP, improved NUMA detection for AMD Ryzen, ubsan support. +* 2020-05-05, `v1.6.3`: stable release 1.6: improved behavior in out-of-memory situations, improved malloc zones on macOS, + build PIC static libraries by default, add option to abort on out-of-memory, line buffered statistics. +* 2020-04-20, `v1.6.2`: stable release 1.6: fix compilation on Android, MingW, Raspberry, and Conda, + stability fix for Windows 7, fix multiple mimalloc instances in one executable, fix `strnlen` overload, + fix aligned debug padding. +* 2020-02-17, `v1.6.1`: stable release 1.6: minor updates (build with clang-cl, fix alignment issue for small objects). +* 2020-02-09, `v1.6.0`: stable release 1.6: fixed potential memory leak, improved overriding + and thread local support on FreeBSD, NetBSD, DragonFly, and macOSX. New byte-precise + heap block overflow detection in debug mode (besides the double-free detection and free-list + corruption detection). Add `nodiscard` attribute to most allocation functions. + Enable `MIMALLOC_PAGE_RESET` by default. New reclamation strategy for abandoned heap pages + for better memory footprint. +* 2020-02-09, `v1.5.0`: stable release 1.5: improved free performance, small bug fixes. +* 2020-01-22, `v1.4.0`: stable release 1.4: improved performance for delayed OS page reset, +more eager concurrent free, addition of STL allocator, fixed potential memory leak. +* 2020-01-15, `v1.3.0`: stable release 1.3: bug fixes, improved randomness and [stronger +free list encoding](https://github.com/microsoft/mimalloc/blob/783e3377f79ee82af43a0793910a9f2d01ac7863/include/mimalloc-internal.h#L396) in secure mode. +* 2019-12-22, `v1.2.2`: stable release 1.2: minor updates. +* 2019-11-22, `v1.2.0`: stable release 1.2: bug fixes, improved secure mode (free list corruption checks, double free mitigation). Improved dynamic overriding on Windows. +* 2019-10-07, `v1.1.0`: stable release 1.1. +* 2019-09-01, `v1.0.8`: pre-release 8: more robust windows dynamic overriding, initial huge page support. +* 2019-08-10, `v1.0.6`: pre-release 6: various performance improvements. diff --git a/Source/mimalloc/src/alloc-aligned.c b/Source/mimalloc/src/alloc-aligned.c index 724c0a1bf..e79a22208 100644 --- a/Source/mimalloc/src/alloc-aligned.c +++ b/Source/mimalloc/src/alloc-aligned.c @@ -6,113 +6,216 @@ terms of the MIT license. A copy of the license can be found in the file -----------------------------------------------------------------------------*/ #include "mimalloc.h" -#include "mimalloc-internal.h" +#include "mimalloc/internal.h" +#include "mimalloc/prim.h" // mi_prim_get_default_heap -#include // memset +#include // memset // ------------------------------------------------------ // Aligned Allocation // ------------------------------------------------------ -static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero) mi_attr_noexcept { - // note: we don't require `size > offset`, we just guarantee that - // the address at offset is aligned regardless of the allocated size. - mi_assert(alignment > 0); - if (mi_unlikely(size > PTRDIFF_MAX)) return NULL; // we don't allocate more than PTRDIFF_MAX (see ) - if (mi_unlikely(alignment==0 || !_mi_is_power_of_two(alignment))) return NULL; // require power-of-two (see ) - const uintptr_t align_mask = alignment-1; // for any x, `(x & align_mask) == (x % alignment)` - - // try if there is a small block available with just the right alignment +// Fallback primitive aligned allocation -- split out for better codegen +static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_fallback(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero) mi_attr_noexcept +{ + mi_assert_internal(size <= PTRDIFF_MAX); + mi_assert_internal(alignment != 0 && _mi_is_power_of_two(alignment)); + + const uintptr_t align_mask = alignment - 1; // for any x, `(x & align_mask) == (x % alignment)` const size_t padsize = size + MI_PADDING_SIZE; - if (mi_likely(padsize <= MI_SMALL_SIZE_MAX)) { - mi_page_t* page = _mi_heap_get_free_small_page(heap,padsize); - const bool is_aligned = (((uintptr_t)page->free+offset) & align_mask)==0; - if (mi_likely(page->free != NULL && is_aligned)) - { - #if MI_STAT>1 - mi_heap_stat_increase( heap, malloc, size); - #endif - void* p = _mi_page_malloc(heap,page,padsize); // TODO: inline _mi_page_malloc - mi_assert_internal(p != NULL); - mi_assert_internal(((uintptr_t)p + offset) % alignment == 0); - if (zero) _mi_block_zero_init(page,p,size); - return p; - } - } // use regular allocation if it is guaranteed to fit the alignment constraints - if (offset==0 && alignment<=padsize && padsize<=MI_MEDIUM_OBJ_SIZE_MAX && (padsize&align_mask)==0) { + if (offset==0 && alignment<=padsize && padsize<=MI_MAX_ALIGN_GUARANTEE && (padsize&align_mask)==0) { void* p = _mi_heap_malloc_zero(heap, size, zero); mi_assert_internal(p == NULL || ((uintptr_t)p % alignment) == 0); return p; } - - // otherwise over-allocate - void* p = _mi_heap_malloc_zero(heap, size + alignment - 1, zero); - if (p == NULL) return NULL; + + void* p; + size_t oversize; + if mi_unlikely(alignment > MI_ALIGNMENT_MAX) { + // use OS allocation for very large alignment and allocate inside a huge page (dedicated segment with 1 page) + // This can support alignments >= MI_SEGMENT_SIZE by ensuring the object can be aligned at a point in the + // first (and single) page such that the segment info is `MI_SEGMENT_SIZE` bytes before it (so it can be found by aligning the pointer down) + if mi_unlikely(offset != 0) { + // todo: cannot support offset alignment for very large alignments yet + #if MI_DEBUG > 0 + _mi_error_message(EOVERFLOW, "aligned allocation with a very large alignment cannot be used with an alignment offset (size %zu, alignment %zu, offset %zu)\n", size, alignment, offset); + #endif + return NULL; + } + oversize = (size <= MI_SMALL_SIZE_MAX ? MI_SMALL_SIZE_MAX + 1 /* ensure we use generic malloc path */ : size); + p = _mi_heap_malloc_zero_ex(heap, oversize, false, alignment); // the page block size should be large enough to align in the single huge page block + // zero afterwards as only the area from the aligned_p may be committed! + if (p == NULL) return NULL; + } + else { + // otherwise over-allocate + oversize = size + alignment - 1; + p = _mi_heap_malloc_zero(heap, oversize, zero); + if (p == NULL) return NULL; + } // .. and align within the allocation - uintptr_t adjust = alignment - (((uintptr_t)p + offset) & align_mask); - mi_assert_internal(adjust <= alignment); - void* aligned_p = (adjust == alignment ? p : (void*)((uintptr_t)p + adjust)); - if (aligned_p != p) mi_page_set_has_aligned(_mi_ptr_page(p), true); + const uintptr_t poffset = ((uintptr_t)p + offset) & align_mask; + const uintptr_t adjust = (poffset == 0 ? 0 : alignment - poffset); + mi_assert_internal(adjust < alignment); + void* aligned_p = (void*)((uintptr_t)p + adjust); + if (aligned_p != p) { + mi_page_t* page = _mi_ptr_page(p); + mi_page_set_has_aligned(page, true); + _mi_padding_shrink(page, (mi_block_t*)p, adjust + size); + } + // todo: expand padding if overallocated ? + + mi_assert_internal(mi_page_usable_block_size(_mi_ptr_page(p)) >= adjust + size); + mi_assert_internal(p == _mi_page_ptr_unalign(_mi_ptr_segment(aligned_p), _mi_ptr_page(aligned_p), aligned_p)); mi_assert_internal(((uintptr_t)aligned_p + offset) % alignment == 0); - mi_assert_internal( p == _mi_page_ptr_unalign(_mi_ptr_segment(aligned_p),_mi_ptr_page(aligned_p),aligned_p) ); + mi_assert_internal(mi_usable_size(aligned_p)>=size); + mi_assert_internal(mi_usable_size(p) == mi_usable_size(aligned_p)+adjust); + + // now zero the block if needed + if (alignment > MI_ALIGNMENT_MAX) { + // for the tracker, on huge aligned allocations only from the start of the large block is defined + mi_track_mem_undefined(aligned_p, size); + if (zero) { + _mi_memzero(aligned_p, mi_usable_size(aligned_p)); + } + } + + if (p != aligned_p) { + mi_track_align(p,aligned_p,adjust,mi_usable_size(aligned_p)); + } return aligned_p; } +// Primitive aligned allocation +static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero) mi_attr_noexcept +{ + // note: we don't require `size > offset`, we just guarantee that the address at offset is aligned regardless of the allocated size. + mi_assert(alignment > 0); + if mi_unlikely(alignment == 0 || !_mi_is_power_of_two(alignment)) { // require power-of-two (see ) + #if MI_DEBUG > 0 + _mi_error_message(EOVERFLOW, "aligned allocation requires the alignment to be a power-of-two (size %zu, alignment %zu)\n", size, alignment); + #endif + return NULL; + } + /* + if mi_unlikely(alignment > MI_ALIGNMENT_MAX) { // we cannot align at a boundary larger than this (or otherwise we cannot find segment headers) + #if MI_DEBUG > 0 + _mi_error_message(EOVERFLOW, "aligned allocation has a maximum alignment of %zu (size %zu, alignment %zu)\n", MI_ALIGNMENT_MAX, size, alignment); + #endif + return NULL; + } + */ + if mi_unlikely(size > PTRDIFF_MAX) { // we don't allocate more than PTRDIFF_MAX (see ) + #if MI_DEBUG > 0 + _mi_error_message(EOVERFLOW, "aligned allocation request is too large (size %zu, alignment %zu)\n", size, alignment); + #endif + return NULL; + } + const uintptr_t align_mask = alignment-1; // for any x, `(x & align_mask) == (x % alignment)` + const size_t padsize = size + MI_PADDING_SIZE; // note: cannot overflow due to earlier size > PTRDIFF_MAX check + + // try first if there happens to be a small block available with just the right alignment + if mi_likely(padsize <= MI_SMALL_SIZE_MAX && alignment <= padsize) { + mi_page_t* page = _mi_heap_get_free_small_page(heap, padsize); + const bool is_aligned = (((uintptr_t)page->free+offset) & align_mask)==0; + if mi_likely(page->free != NULL && is_aligned) + { + #if MI_STAT>1 + mi_heap_stat_increase(heap, malloc, size); + #endif + void* p = _mi_page_malloc(heap, page, padsize, zero); // TODO: inline _mi_page_malloc + mi_assert_internal(p != NULL); + mi_assert_internal(((uintptr_t)p + offset) % alignment == 0); + mi_track_malloc(p,size,zero); + return p; + } + } + // fallback + return mi_heap_malloc_zero_aligned_at_fallback(heap, size, alignment, offset, zero); +} + + +// ------------------------------------------------------ +// Optimized mi_heap_malloc_aligned / mi_malloc_aligned +// ------------------------------------------------------ -mi_decl_restrict void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, false); } -mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_malloc_aligned_at(heap, size, alignment, 0); +mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { + #if !MI_PADDING + // without padding, any small sized allocation is naturally aligned (see also `_mi_segment_page_start`) + if (!_mi_is_power_of_two(alignment)) return NULL; + if mi_likely(_mi_is_power_of_two(size) && size >= alignment && size <= MI_SMALL_SIZE_MAX) + #else + // with padding, we can only guarantee this for fixed alignments + if mi_likely((alignment == sizeof(void*) || (alignment == MI_MAX_ALIGN_SIZE && size > (MI_MAX_ALIGN_SIZE/2))) + && size <= MI_SMALL_SIZE_MAX) + #endif + { + // fast path for common alignment and size + return mi_heap_malloc_small(heap, size); + } + else { + return mi_heap_malloc_aligned_at(heap, size, alignment, 0); + } } -mi_decl_restrict void* mi_heap_zalloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +// ------------------------------------------------------ +// Aligned Allocation +// ------------------------------------------------------ + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, true); } -mi_decl_restrict void* mi_heap_zalloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { return mi_heap_zalloc_aligned_at(heap, size, alignment, 0); } -mi_decl_restrict void* mi_heap_calloc_aligned_at(mi_heap_t* heap, size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned_at(mi_heap_t* heap, size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; return mi_heap_zalloc_aligned_at(heap, total, alignment, offset); } -mi_decl_restrict void* mi_heap_calloc_aligned(mi_heap_t* heap, size_t count, size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned(mi_heap_t* heap, size_t count, size_t size, size_t alignment) mi_attr_noexcept { return mi_heap_calloc_aligned_at(heap,count,size,alignment,0); } -mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_malloc_aligned_at(mi_get_default_heap(), size, alignment, offset); +mi_decl_nodiscard mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_heap_malloc_aligned_at(mi_prim_get_default_heap(), size, alignment, offset); } -mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_malloc_aligned(mi_get_default_heap(), size, alignment); +mi_decl_nodiscard mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept { + return mi_heap_malloc_aligned(mi_prim_get_default_heap(), size, alignment); } -mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_zalloc_aligned_at(mi_get_default_heap(), size, alignment, offset); +mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_heap_zalloc_aligned_at(mi_prim_get_default_heap(), size, alignment, offset); } -mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_zalloc_aligned(mi_get_default_heap(), size, alignment); +mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t alignment) mi_attr_noexcept { + return mi_heap_zalloc_aligned(mi_prim_get_default_heap(), size, alignment); } -mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_calloc_aligned_at(mi_get_default_heap(), count, size, alignment, offset); +mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_heap_calloc_aligned_at(mi_prim_get_default_heap(), count, size, alignment, offset); } -mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_calloc_aligned(mi_get_default_heap(), count, size, alignment); +mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t size, size_t alignment) mi_attr_noexcept { + return mi_heap_calloc_aligned(mi_prim_get_default_heap(), count, size, alignment); } +// ------------------------------------------------------ +// Aligned re-allocation +// ------------------------------------------------------ + static void* mi_heap_realloc_zero_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset, bool zero) mi_attr_noexcept { mi_assert(alignment > 0); if (alignment <= sizeof(uintptr_t)) return _mi_heap_realloc_zero(heap,p,newsize,zero); @@ -151,55 +254,54 @@ static void* mi_heap_realloc_zero_aligned(mi_heap_t* heap, void* p, size_t newsi return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,zero); } -void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,false); } -void* mi_heap_realloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_realloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { return mi_heap_realloc_zero_aligned(heap,p,newsize,alignment,false); } -void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_realloc_zero_aligned_at(heap, p, newsize, alignment, offset, true); } -void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { return mi_heap_realloc_zero_aligned(heap, p, newsize, alignment, true); } -void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(newcount, size, &total)) return NULL; return mi_heap_rezalloc_aligned_at(heap, p, total, alignment, offset); } -void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(newcount, size, &total)) return NULL; return mi_heap_rezalloc_aligned(heap, p, total, alignment); } -void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_realloc_aligned_at(mi_get_default_heap(), p, newsize, alignment, offset); +mi_decl_nodiscard void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_heap_realloc_aligned_at(mi_prim_get_default_heap(), p, newsize, alignment, offset); } -void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept { - return mi_heap_realloc_aligned(mi_get_default_heap(), p, newsize, alignment); +mi_decl_nodiscard void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept { + return mi_heap_realloc_aligned(mi_prim_get_default_heap(), p, newsize, alignment); } -void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_rezalloc_aligned_at(mi_get_default_heap(), p, newsize, alignment, offset); +mi_decl_nodiscard void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_heap_rezalloc_aligned_at(mi_prim_get_default_heap(), p, newsize, alignment, offset); } -void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept { - return mi_heap_rezalloc_aligned(mi_get_default_heap(), p, newsize, alignment); +mi_decl_nodiscard void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept { + return mi_heap_rezalloc_aligned(mi_prim_get_default_heap(), p, newsize, alignment); } -void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { - return mi_heap_recalloc_aligned_at(mi_get_default_heap(), p, newcount, size, alignment, offset); +mi_decl_nodiscard void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { + return mi_heap_recalloc_aligned_at(mi_prim_get_default_heap(), p, newcount, size, alignment, offset); } -void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { - return mi_heap_recalloc_aligned(mi_get_default_heap(), p, newcount, size, alignment); +mi_decl_nodiscard void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { + return mi_heap_recalloc_aligned(mi_prim_get_default_heap(), p, newcount, size, alignment); } - diff --git a/Source/mimalloc/src/alloc-override-osx.c b/Source/mimalloc/src/alloc-override-osx.c deleted file mode 100644 index f506d30a9..000000000 --- a/Source/mimalloc/src/alloc-override-osx.c +++ /dev/null @@ -1,281 +0,0 @@ -/* ---------------------------------------------------------------------------- -Copyright (c) 2018-2020, Microsoft Research, Daan Leijen -This is free software; you can redistribute it and/or modify it under the -terms of the MIT license. A copy of the license can be found in the file -"LICENSE" at the root of this distribution. ------------------------------------------------------------------------------*/ - -#include "mimalloc.h" -#include "mimalloc-internal.h" - -#if defined(MI_MALLOC_OVERRIDE) - -#if !defined(__APPLE__) -#error "this file should only be included on macOS" -#endif - -/* ------------------------------------------------------ - Override system malloc on macOS - This is done through the malloc zone interface. - It seems we also need to interpose (see `alloc-override.c`) - or otherwise we get zone errors as there are usually - already allocations done by the time we take over the - zone. Unfortunately, that means we need to replace - the `free` with a checked free (`cfree`) impacting - performance. ------------------------------------------------------- */ - -#include -#include -#include // memset - -#if defined(MAC_OS_X_VERSION_10_6) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 -// only available from OSX 10.6 -extern malloc_zone_t* malloc_default_purgeable_zone(void) __attribute__((weak_import)); -#endif - -/* ------------------------------------------------------ - malloc zone members ------------------------------------------------------- */ - -static size_t zone_size(malloc_zone_t* zone, const void* p) { - UNUSED(zone); - if (!mi_is_in_heap_region(p)) - return 0; // not our pointer, bail out - - return mi_usable_size(p); -} - -static void* zone_malloc(malloc_zone_t* zone, size_t size) { - UNUSED(zone); - return mi_malloc(size); -} - -static void* zone_calloc(malloc_zone_t* zone, size_t count, size_t size) { - UNUSED(zone); - return mi_calloc(count, size); -} - -static void* zone_valloc(malloc_zone_t* zone, size_t size) { - UNUSED(zone); - return mi_malloc_aligned(size, _mi_os_page_size()); -} - -static void zone_free(malloc_zone_t* zone, void* p) { - UNUSED(zone); - mi_free(p); -} - -static void* zone_realloc(malloc_zone_t* zone, void* p, size_t newsize) { - UNUSED(zone); - return mi_realloc(p, newsize); -} - -static void* zone_memalign(malloc_zone_t* zone, size_t alignment, size_t size) { - UNUSED(zone); - return mi_malloc_aligned(size,alignment); -} - -static void zone_destroy(malloc_zone_t* zone) { - UNUSED(zone); - // todo: ignore for now? -} - -static unsigned zone_batch_malloc(malloc_zone_t* zone, size_t size, void** ps, unsigned count) { - size_t i; - for (i = 0; i < count; i++) { - ps[i] = zone_malloc(zone, size); - if (ps[i] == NULL) break; - } - return i; -} - -static void zone_batch_free(malloc_zone_t* zone, void** ps, unsigned count) { - for(size_t i = 0; i < count; i++) { - zone_free(zone, ps[i]); - ps[i] = NULL; - } -} - -static size_t zone_pressure_relief(malloc_zone_t* zone, size_t size) { - UNUSED(zone); UNUSED(size); - mi_collect(false); - return 0; -} - -static void zone_free_definite_size(malloc_zone_t* zone, void* p, size_t size) { - UNUSED(size); - zone_free(zone,p); -} - - -/* ------------------------------------------------------ - Introspection members ------------------------------------------------------- */ - -static kern_return_t intro_enumerator(task_t task, void* p, - unsigned type_mask, vm_address_t zone_address, - memory_reader_t reader, - vm_range_recorder_t recorder) -{ - // todo: enumerate all memory - UNUSED(task); UNUSED(p); UNUSED(type_mask); UNUSED(zone_address); - UNUSED(reader); UNUSED(recorder); - return KERN_SUCCESS; -} - -static size_t intro_good_size(malloc_zone_t* zone, size_t size) { - UNUSED(zone); - return mi_good_size(size); -} - -static boolean_t intro_check(malloc_zone_t* zone) { - UNUSED(zone); - return true; -} - -static void intro_print(malloc_zone_t* zone, boolean_t verbose) { - UNUSED(zone); UNUSED(verbose); - mi_stats_print(NULL); -} - -static void intro_log(malloc_zone_t* zone, void* p) { - UNUSED(zone); UNUSED(p); - // todo? -} - -static void intro_force_lock(malloc_zone_t* zone) { - UNUSED(zone); - // todo? -} - -static void intro_force_unlock(malloc_zone_t* zone) { - UNUSED(zone); - // todo? -} - -static void intro_statistics(malloc_zone_t* zone, malloc_statistics_t* stats) { - UNUSED(zone); - // todo... - stats->blocks_in_use = 0; - stats->size_in_use = 0; - stats->max_size_in_use = 0; - stats->size_allocated = 0; -} - -static boolean_t intro_zone_locked(malloc_zone_t* zone) { - UNUSED(zone); - return false; -} - - -/* ------------------------------------------------------ - At process start, override the default allocator ------------------------------------------------------- */ - -static malloc_zone_t* mi_get_default_zone() -{ - // The first returned zone is the real default - malloc_zone_t** zones = NULL; - unsigned count = 0; - kern_return_t ret = malloc_get_all_zones(0, NULL, (vm_address_t**)&zones, &count); - if (ret == KERN_SUCCESS && count > 0) { - return zones[0]; - } - else { - // fallback - return malloc_default_zone(); - } -} - -static malloc_introspection_t mi_introspect = { - .enumerator = &intro_enumerator, - .good_size = &intro_good_size, - .check = &intro_check, - .print = &intro_print, - .log = &intro_log, - .force_lock = &intro_force_lock, - .force_unlock = &intro_force_unlock, -#if defined(MAC_OS_X_VERSION_10_6) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - .zone_locked = &intro_zone_locked, - .statistics = &intro_statistics, -#endif -}; - -static malloc_zone_t mi_malloc_zone = { - .size = &zone_size, - .zone_name = "mimalloc", - .introspect = &mi_introspect, - .malloc = &zone_malloc, - .calloc = &zone_calloc, - .valloc = &zone_valloc, - .free = &zone_free, - .realloc = &zone_realloc, - .destroy = &zone_destroy, - .batch_malloc = &zone_batch_malloc, - .batch_free = &zone_batch_free, -#if defined(MAC_OS_X_VERSION_10_6) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - // switch to version 9 on OSX 10.6 to support memalign. - .version = 9, - .memalign = &zone_memalign, - .free_definite_size = &zone_free_definite_size, - .pressure_relief = &zone_pressure_relief, -#else - .version = 4, -#endif -}; - - -#if defined(MI_SHARED_LIB_EXPORT) && defined(MI_INTERPOSE) - -static malloc_zone_t *mi_malloc_default_zone(void) { - return &mi_malloc_zone; -} -// TODO: should use the macros in alloc-override but they aren't available here. -__attribute__((used)) static struct { - const void *replacement; - const void *target; -} replace_malloc_default_zone[] __attribute__((section("__DATA, __interpose"))) = { - { (const void*)mi_malloc_default_zone, (const void*)malloc_default_zone }, -}; -#endif - -static void __attribute__((constructor(0))) _mi_macos_override_malloc() { - malloc_zone_t* purgeable_zone = NULL; - -#if defined(MAC_OS_X_VERSION_10_6) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - // force the purgeable zone to exist to avoid strange bugs - if (malloc_default_purgeable_zone) { - purgeable_zone = malloc_default_purgeable_zone(); - } -#endif - - // Register our zone. - // thomcc: I think this is still needed to put us in the zone list. - malloc_zone_register(&mi_malloc_zone); - // Unregister the default zone, this makes our zone the new default - // as that was the last registered. - malloc_zone_t *default_zone = mi_get_default_zone(); - // thomcc: Unsure if the next test is *always* false or just false in the - // cases I've tried. I'm also unsure if the code inside is needed. at all - if (default_zone != &mi_malloc_zone) { - malloc_zone_unregister(default_zone); - - // Reregister the default zone so free and realloc in that zone keep working. - malloc_zone_register(default_zone); - } - - // Unregister, and re-register the purgeable_zone to avoid bugs if it occurs - // earlier than the default zone. - if (purgeable_zone != NULL) { - malloc_zone_unregister(purgeable_zone); - malloc_zone_register(purgeable_zone); - } - -} - -#endif // MI_MALLOC_OVERRIDE diff --git a/Source/mimalloc/src/alloc-override.c b/Source/mimalloc/src/alloc-override.c index 6a87e7bd2..40098ac58 100644 --- a/Source/mimalloc/src/alloc-override.c +++ b/Source/mimalloc/src/alloc-override.c @@ -13,15 +13,26 @@ terms of the MIT license. A copy of the license can be found in the file #error "It is only possible to override "malloc" on Windows when building as a DLL (and linking the C runtime as a DLL)" #endif -#if defined(MI_MALLOC_OVERRIDE) && !(defined(_WIN32)) // || (defined(__APPLE__) && !defined(MI_INTERPOSE))) +#if defined(MI_MALLOC_OVERRIDE) && !(defined(_WIN32)) + +#if defined(__APPLE__) +#include +mi_decl_externc void vfree(void* p); +mi_decl_externc size_t malloc_size(const void* p); +mi_decl_externc size_t malloc_good_size(size_t size); +#endif + +// helper definition for C override of C++ new +typedef struct mi_nothrow_s { int _tag; } mi_nothrow_t; // ------------------------------------------------------ // Override system malloc // ------------------------------------------------------ -#if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) - // use aliasing to alias the exported function to one of our `mi_` functions +#if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) && !MI_TRACK_ENABLED + // gcc, clang: use aliasing to alias the exported function to one of our `mi_` functions #if (defined(__GNUC__) && __GNUC__ >= 9) + #pragma GCC diagnostic ignored "-Wattributes" // or we get warnings that nodiscard is ignored on a forward #define MI_FORWARD(fun) __attribute__((alias(#fun), used, visibility("default"), copy(fun))); #else #define MI_FORWARD(fun) __attribute__((alias(#fun), used, visibility("default"))); @@ -32,7 +43,7 @@ terms of the MIT license. A copy of the license can be found in the file #define MI_FORWARD0(fun,x) MI_FORWARD(fun) #define MI_FORWARD02(fun,x,y) MI_FORWARD(fun) #else - // use forwarding by calling our `mi_` function + // otherwise use forwarding by calling our `mi_` function #define MI_FORWARD1(fun,x) { return fun(x); } #define MI_FORWARD2(fun,x,y) { return fun(x,y); } #define MI_FORWARD3(fun,x,y,z) { return fun(x,y,z); } @@ -40,7 +51,17 @@ terms of the MIT license. A copy of the license can be found in the file #define MI_FORWARD02(fun,x,y) { fun(x,y); } #endif -#if defined(__APPLE__) && defined(MI_SHARED_LIB_EXPORT) && defined(MI_INTERPOSE) + +#if defined(__APPLE__) && defined(MI_SHARED_LIB_EXPORT) && defined(MI_OSX_INTERPOSE) + // define MI_OSX_IS_INTERPOSED as we should not provide forwarding definitions for + // functions that are interposed (or the interposing does not work) + #define MI_OSX_IS_INTERPOSED + + mi_decl_externc size_t mi_malloc_size_checked(void *p) { + if (!mi_is_in_heap_region(p)) return 0; + return mi_usable_size(p); + } + // use interposing so `DYLD_INSERT_LIBRARIES` works without `DYLD_FORCE_FLAT_NAMESPACE=1` // See: struct mi_interpose_s { @@ -49,6 +70,7 @@ terms of the MIT license. A copy of the license can be found in the file }; #define MI_INTERPOSE_FUN(oldfun,newfun) { (const void*)&newfun, (const void*)&oldfun } #define MI_INTERPOSE_MI(fun) MI_INTERPOSE_FUN(fun,mi_##fun) + __attribute__((used)) static struct mi_interpose_s _mi_interposes[] __attribute__((section("__DATA, __interpose"))) = { MI_INTERPOSE_MI(malloc), @@ -60,25 +82,57 @@ terms of the MIT license. A copy of the license can be found in the file MI_INTERPOSE_MI(posix_memalign), MI_INTERPOSE_MI(reallocf), MI_INTERPOSE_MI(valloc), - #ifndef MI_OSX_ZONE - // some code allocates from default zone but deallocates using plain free :-( (like NxHashResizeToCapacity ) - MI_INTERPOSE_FUN(free,mi_cfree), // use safe free that checks if pointers are from us - #else - // We interpose malloc_default_zone in alloc-override-osx.c - MI_INTERPOSE_MI(free), + MI_INTERPOSE_FUN(malloc_size,mi_malloc_size_checked), + MI_INTERPOSE_MI(malloc_good_size), + #if defined(MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15 + MI_INTERPOSE_MI(aligned_alloc), #endif - // some code allocates from a zone but deallocates using plain free :-( (like NxHashResizeToCapacity ) + #ifdef MI_OSX_ZONE + // we interpose malloc_default_zone in alloc-override-osx.c so we can use mi_free safely + MI_INTERPOSE_MI(free), + MI_INTERPOSE_FUN(vfree,mi_free), + #else + // sometimes code allocates from default zone but deallocates using plain free :-( (like NxHashResizeToCapacity ) MI_INTERPOSE_FUN(free,mi_cfree), // use safe free that checks if pointers are from us + MI_INTERPOSE_FUN(vfree,mi_cfree), + #endif + }; + + #ifdef __cplusplus + extern "C" { + #endif + void _ZdlPv(void* p); // delete + void _ZdaPv(void* p); // delete[] + void _ZdlPvm(void* p, size_t n); // delete + void _ZdaPvm(void* p, size_t n); // delete[] + void* _Znwm(size_t n); // new + void* _Znam(size_t n); // new[] + void* _ZnwmRKSt9nothrow_t(size_t n, mi_nothrow_t tag); // new nothrow + void* _ZnamRKSt9nothrow_t(size_t n, mi_nothrow_t tag); // new[] nothrow + #ifdef __cplusplus + } + #endif + __attribute__((used)) static struct mi_interpose_s _mi_cxx_interposes[] __attribute__((section("__DATA, __interpose"))) = + { + MI_INTERPOSE_FUN(_ZdlPv,mi_free), + MI_INTERPOSE_FUN(_ZdaPv,mi_free), + MI_INTERPOSE_FUN(_ZdlPvm,mi_free_size), + MI_INTERPOSE_FUN(_ZdaPvm,mi_free_size), + MI_INTERPOSE_FUN(_Znwm,mi_new), + MI_INTERPOSE_FUN(_Znam,mi_new), + MI_INTERPOSE_FUN(_ZnwmRKSt9nothrow_t,mi_new_nothrow), + MI_INTERPOSE_FUN(_ZnamRKSt9nothrow_t,mi_new_nothrow), }; + #elif defined(_MSC_VER) // cannot override malloc unless using a dll. // we just override new/delete which does work in a static library. #else // On all other systems forward to our API - void* malloc(size_t size) MI_FORWARD1(mi_malloc, size) - void* calloc(size_t size, size_t n) MI_FORWARD2(mi_calloc, size, n) - void* realloc(void* p, size_t newsize) MI_FORWARD2(mi_realloc, p, newsize) - void free(void* p) MI_FORWARD0(mi_free, p) + mi_decl_export void* malloc(size_t size) MI_FORWARD1(mi_malloc, size) + mi_decl_export void* calloc(size_t size, size_t n) MI_FORWARD2(mi_calloc, size, n) + mi_decl_export void* realloc(void* p, size_t newsize) MI_FORWARD2(mi_realloc, p, newsize) + mi_decl_export void free(void* p) MI_FORWARD0(mi_free, p) #endif #if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) @@ -96,18 +150,21 @@ terms of the MIT license. A copy of the license can be found in the file // see // ------------------------------------------------------ #include - void operator delete(void* p) noexcept MI_FORWARD0(mi_free,p) - void operator delete[](void* p) noexcept MI_FORWARD0(mi_free,p) - void* operator new(std::size_t n) noexcept(false) MI_FORWARD1(mi_new,n) - void* operator new[](std::size_t n) noexcept(false) MI_FORWARD1(mi_new,n) + #ifndef MI_OSX_IS_INTERPOSED + void operator delete(void* p) noexcept MI_FORWARD0(mi_free,p) + void operator delete[](void* p) noexcept MI_FORWARD0(mi_free,p) - void* operator new (std::size_t n, const std::nothrow_t& tag) noexcept { UNUSED(tag); return mi_new_nothrow(n); } - void* operator new[](std::size_t n, const std::nothrow_t& tag) noexcept { UNUSED(tag); return mi_new_nothrow(n); } + void* operator new(std::size_t n) noexcept(false) MI_FORWARD1(mi_new,n) + void* operator new[](std::size_t n) noexcept(false) MI_FORWARD1(mi_new,n) - #if (__cplusplus >= 201402L || _MSC_VER >= 1916) - void operator delete (void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n) - void operator delete[](void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n) + void* operator new (std::size_t n, const std::nothrow_t& tag) noexcept { MI_UNUSED(tag); return mi_new_nothrow(n); } + void* operator new[](std::size_t n, const std::nothrow_t& tag) noexcept { MI_UNUSED(tag); return mi_new_nothrow(n); } + + #if (__cplusplus >= 201402L || _MSC_VER >= 1916) + void operator delete (void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n) + void operator delete[](void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n) + #endif #endif #if (__cplusplus > 201402L && defined(__cpp_aligned_new)) && (!defined(__GNUC__) || (__GNUC__ > 5)) @@ -115,6 +172,8 @@ terms of the MIT license. A copy of the license can be found in the file void operator delete[](void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast(al)); } void operator delete (void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast(al)); }; void operator delete[](void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast(al)); }; + void operator delete (void* p, std::align_val_t al, const std::nothrow_t&) noexcept { mi_free_aligned(p, static_cast(al)); } + void operator delete[](void* p, std::align_val_t al, const std::nothrow_t&) noexcept { mi_free_aligned(p, static_cast(al)); } void* operator new( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast(al)); } void* operator new[]( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast(al)); } @@ -128,6 +187,7 @@ terms of the MIT license. A copy of the license can be found in the file // used by GCC and CLang). // See // ------------------------------------------------------ + void _ZdlPv(void* p) MI_FORWARD0(mi_free,p) // delete void _ZdaPv(void* p) MI_FORWARD0(mi_free,p) // delete[] void _ZdlPvm(void* p, size_t n) MI_FORWARD02(mi_free_size,p,n) @@ -137,77 +197,90 @@ terms of the MIT license. A copy of the license can be found in the file void _ZdlPvmSt11align_val_t(void* p, size_t n, size_t al) { mi_free_size_aligned(p,n,al); } void _ZdaPvmSt11align_val_t(void* p, size_t n, size_t al) { mi_free_size_aligned(p,n,al); } - typedef struct mi_nothrow_s { int _tag; } mi_nothrow_t; #if (MI_INTPTR_SIZE==8) void* _Znwm(size_t n) MI_FORWARD1(mi_new,n) // new 64-bit void* _Znam(size_t n) MI_FORWARD1(mi_new,n) // new[] 64-bit + void* _ZnwmRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); } + void* _ZnamRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); } void* _ZnwmSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al) void* _ZnamSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al) - void* _ZnwmRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { UNUSED(tag); return mi_new_nothrow(n); } - void* _ZnamRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { UNUSED(tag); return mi_new_nothrow(n); } - void* _ZnwmSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { UNUSED(tag); return mi_new_aligned_nothrow(n,al); } - void* _ZnamSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { UNUSED(tag); return mi_new_aligned_nothrow(n,al); } + void* _ZnwmSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); } + void* _ZnamSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); } #elif (MI_INTPTR_SIZE==4) void* _Znwj(size_t n) MI_FORWARD1(mi_new,n) // new 64-bit void* _Znaj(size_t n) MI_FORWARD1(mi_new,n) // new[] 64-bit + void* _ZnwjRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); } + void* _ZnajRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); } void* _ZnwjSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al) void* _ZnajSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al) - void* _ZnwjRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { UNUSED(tag); return mi_new_nothrow(n); } - void* _ZnajRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { UNUSED(tag); return mi_new_nothrow(n); } - void* _ZnwjSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { UNUSED(tag); return mi_new_aligned_nothrow(n,al); } - void* _ZnajSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { UNUSED(tag); return mi_new_aligned_nothrow(n,al); } + void* _ZnwjSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); } + void* _ZnajSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); } #else - #error "define overloads for new/delete for this platform (just for performance, can be skipped)" + #error "define overloads for new/delete for this platform (just for performance, can be skipped)" #endif #endif // __cplusplus +// ------------------------------------------------------ +// Further Posix & Unix functions definitions +// ------------------------------------------------------ #ifdef __cplusplus extern "C" { #endif -// ------------------------------------------------------ -// Posix & Unix functions definitions -// ------------------------------------------------------ +#ifndef MI_OSX_IS_INTERPOSED + // Forward Posix/Unix calls as well + void* reallocf(void* p, size_t newsize) MI_FORWARD2(mi_reallocf,p,newsize) + size_t malloc_size(const void* p) MI_FORWARD1(mi_usable_size,p) + #if !defined(__ANDROID__) && !defined(__FreeBSD__) + size_t malloc_usable_size(void *p) MI_FORWARD1(mi_usable_size,p) + #else + size_t malloc_usable_size(const void *p) MI_FORWARD1(mi_usable_size,p) + #endif -void cfree(void* p) MI_FORWARD0(mi_free, p) -void* reallocf(void* p, size_t newsize) MI_FORWARD2(mi_reallocf,p,newsize) -size_t malloc_size(const void* p) MI_FORWARD1(mi_usable_size,p) -#if !defined(__ANDROID__) -size_t malloc_usable_size(void *p) MI_FORWARD1(mi_usable_size,p) -#else -size_t malloc_usable_size(const void *p) MI_FORWARD1(mi_usable_size,p) + // No forwarding here due to aliasing/name mangling issues + void* valloc(size_t size) { return mi_valloc(size); } + void vfree(void* p) { mi_free(p); } + size_t malloc_good_size(size_t size) { return mi_malloc_good_size(size); } + int posix_memalign(void** p, size_t alignment, size_t size) { return mi_posix_memalign(p, alignment, size); } + + // `aligned_alloc` is only available when __USE_ISOC11 is defined. + // Note: Conda has a custom glibc where `aligned_alloc` is declared `static inline` and we cannot + // override it, but both _ISOC11_SOURCE and __USE_ISOC11 are undefined in Conda GCC7 or GCC9. + // Fortunately, in the case where `aligned_alloc` is declared as `static inline` it + // uses internally `memalign`, `posix_memalign`, or `_aligned_malloc` so we can avoid overriding it ourselves. + #if __USE_ISOC11 + void* aligned_alloc(size_t alignment, size_t size) { return mi_aligned_alloc(alignment, size); } + #endif #endif // no forwarding here due to aliasing/name mangling issues -void* valloc(size_t size) { return mi_valloc(size); } -void* pvalloc(size_t size) { return mi_pvalloc(size); } -void* reallocarray(void* p, size_t count, size_t size) { return mi_reallocarray(p, count, size); } -void* memalign(size_t alignment, size_t size) { return mi_memalign(alignment, size); } -int posix_memalign(void** p, size_t alignment, size_t size) { return mi_posix_memalign(p, alignment, size); } -void* _aligned_malloc(size_t alignment, size_t size) { return mi_aligned_alloc(alignment, size); } - -// `aligned_alloc` is only available when __USE_ISOC11 is defined. -// Note: Conda has a custom glibc where `aligned_alloc` is declared `static inline` and we cannot -// override it, but both _ISOC11_SOURCE and __USE_ISOC11 are undefined in Conda GCC7 or GCC9. -// Fortunately, in the case where `aligned_alloc` is declared as `static inline` it -// uses internally `memalign`, `posix_memalign`, or `_aligned_malloc` so we can avoid overriding it ourselves. -#if __USE_ISOC11 -void* aligned_alloc(size_t alignment, size_t size) { return mi_aligned_alloc(alignment, size); } -#endif +void cfree(void* p) { mi_free(p); } +void* pvalloc(size_t size) { return mi_pvalloc(size); } +void* reallocarray(void* p, size_t count, size_t size) { return mi_reallocarray(p, count, size); } +int reallocarr(void* p, size_t count, size_t size) { return mi_reallocarr(p, count, size); } +void* memalign(size_t alignment, size_t size) { return mi_memalign(alignment, size); } +void* _aligned_malloc(size_t alignment, size_t size) { return mi_aligned_alloc(alignment, size); } +#if defined(__wasi__) + // forward __libc interface (see PR #667) + void* __libc_malloc(size_t size) MI_FORWARD1(mi_malloc, size) + void* __libc_calloc(size_t count, size_t size) MI_FORWARD2(mi_calloc, count, size) + void* __libc_realloc(void* p, size_t size) MI_FORWARD2(mi_realloc, p, size) + void __libc_free(void* p) MI_FORWARD0(mi_free, p) + void* __libc_memalign(size_t alignment, size_t size) { return mi_memalign(alignment, size); } -#if defined(__GLIBC__) && defined(__linux__) +#elif defined(__GLIBC__) && defined(__linux__) // forward __libc interface (needed for glibc-based Linux distributions) - void* __libc_malloc(size_t size) MI_FORWARD1(mi_malloc,size) - void* __libc_calloc(size_t count, size_t size) MI_FORWARD2(mi_calloc,count,size) - void* __libc_realloc(void* p, size_t size) MI_FORWARD2(mi_realloc,p,size) - void __libc_free(void* p) MI_FORWARD0(mi_free,p) - void __libc_cfree(void* p) MI_FORWARD0(mi_free,p) - - void* __libc_valloc(size_t size) { return mi_valloc(size); } - void* __libc_pvalloc(size_t size) { return mi_pvalloc(size); } - void* __libc_memalign(size_t alignment, size_t size) { return mi_memalign(alignment,size); } + void* __libc_malloc(size_t size) MI_FORWARD1(mi_malloc,size) + void* __libc_calloc(size_t count, size_t size) MI_FORWARD2(mi_calloc,count,size) + void* __libc_realloc(void* p, size_t size) MI_FORWARD2(mi_realloc,p,size) + void __libc_free(void* p) MI_FORWARD0(mi_free,p) + void __libc_cfree(void* p) MI_FORWARD0(mi_free,p) + + void* __libc_valloc(size_t size) { return mi_valloc(size); } + void* __libc_pvalloc(size_t size) { return mi_pvalloc(size); } + void* __libc_memalign(size_t alignment, size_t size) { return mi_memalign(alignment,size); } int __posix_memalign(void** p, size_t alignment, size_t size) { return mi_posix_memalign(p,alignment,size); } #endif diff --git a/Source/mimalloc/src/alloc-posix.c b/Source/mimalloc/src/alloc-posix.c index 43931e56d..b6f09d1a1 100644 --- a/Source/mimalloc/src/alloc-posix.c +++ b/Source/mimalloc/src/alloc-posix.c @@ -10,7 +10,7 @@ terms of the MIT license. A copy of the license can be found in the file // for convenience and used when overriding these functions. // ------------------------------------------------------------------------ #include "mimalloc.h" -#include "mimalloc-internal.h" +#include "mimalloc/internal.h" // ------------------------------------------------------ // Posix & Unix functions definitions @@ -32,14 +32,20 @@ terms of the MIT license. A copy of the license can be found in the file #endif -size_t mi_malloc_size(const void* p) mi_attr_noexcept { +mi_decl_nodiscard size_t mi_malloc_size(const void* p) mi_attr_noexcept { + // if (!mi_is_in_heap_region(p)) return 0; return mi_usable_size(p); } -size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept { +mi_decl_nodiscard size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept { + // if (!mi_is_in_heap_region(p)) return 0; return mi_usable_size(p); } +mi_decl_nodiscard size_t mi_malloc_good_size(size_t size) mi_attr_noexcept { + return mi_good_size(size); +} + void mi_cfree(void* p) mi_attr_noexcept { if (mi_is_in_heap_region(p)) { mi_free(p); @@ -50,53 +56,74 @@ int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept // Note: The spec dictates we should not modify `*p` on an error. (issue#27) // if (p == NULL) return EINVAL; - if (alignment % sizeof(void*) != 0) return EINVAL; // natural alignment - if (!_mi_is_power_of_two(alignment)) return EINVAL; // not a power of 2 - void* q = (mi_malloc_satisfies_alignment(alignment, size) ? mi_malloc(size) : mi_malloc_aligned(size, alignment)); + if (alignment % sizeof(void*) != 0) return EINVAL; // natural alignment + if (alignment==0 || !_mi_is_power_of_two(alignment)) return EINVAL; // not a power of 2 + void* q = mi_malloc_aligned(size, alignment); if (q==NULL && size != 0) return ENOMEM; mi_assert_internal(((uintptr_t)q % alignment) == 0); *p = q; return 0; } -mi_decl_restrict void* mi_memalign(size_t alignment, size_t size) mi_attr_noexcept { - void* p = (mi_malloc_satisfies_alignment(alignment,size) ? mi_malloc(size) : mi_malloc_aligned(size, alignment)); +mi_decl_nodiscard mi_decl_restrict void* mi_memalign(size_t alignment, size_t size) mi_attr_noexcept { + void* p = mi_malloc_aligned(size, alignment); mi_assert_internal(((uintptr_t)p % alignment) == 0); return p; } -mi_decl_restrict void* mi_valloc(size_t size) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_valloc(size_t size) mi_attr_noexcept { return mi_memalign( _mi_os_page_size(), size ); } -mi_decl_restrict void* mi_pvalloc(size_t size) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_pvalloc(size_t size) mi_attr_noexcept { size_t psize = _mi_os_page_size(); if (size >= SIZE_MAX - psize) return NULL; // overflow size_t asize = _mi_align_up(size, psize); return mi_malloc_aligned(asize, psize); } -mi_decl_restrict void* mi_aligned_alloc(size_t alignment, size_t size) mi_attr_noexcept { - if (alignment==0 || !_mi_is_power_of_two(alignment)) return NULL; - if ((size&(alignment-1)) != 0) return NULL; // C11 requires integral multiple, see - void* p = (mi_malloc_satisfies_alignment(alignment, size) ? mi_malloc(size) : mi_malloc_aligned(size, alignment)); +mi_decl_nodiscard mi_decl_restrict void* mi_aligned_alloc(size_t alignment, size_t size) mi_attr_noexcept { + // C11 requires the size to be an integral multiple of the alignment, see . + // unfortunately, it turns out quite some programs pass a size that is not an integral multiple so skip this check.. + /* if mi_unlikely((size & (alignment - 1)) != 0) { // C11 requires alignment>0 && integral multiple, see + #if MI_DEBUG > 0 + _mi_error_message(EOVERFLOW, "(mi_)aligned_alloc requires the size to be an integral multiple of the alignment (size %zu, alignment %zu)\n", size, alignment); + #endif + return NULL; + } + */ + // C11 also requires alignment to be a power-of-two (and > 0) which is checked in mi_malloc_aligned + void* p = mi_malloc_aligned(size, alignment); mi_assert_internal(((uintptr_t)p % alignment) == 0); return p; } -void* mi_reallocarray( void* p, size_t count, size_t size ) mi_attr_noexcept { // BSD +mi_decl_nodiscard void* mi_reallocarray( void* p, size_t count, size_t size ) mi_attr_noexcept { // BSD void* newp = mi_reallocn(p,count,size); - if (newp==NULL) errno = ENOMEM; + if (newp==NULL) { errno = ENOMEM; } return newp; } +mi_decl_nodiscard int mi_reallocarr( void* p, size_t count, size_t size ) mi_attr_noexcept { // NetBSD + mi_assert(p != NULL); + if (p == NULL) { + errno = EINVAL; + return EINVAL; + } + void** op = (void**)p; + void* newp = mi_reallocarray(*op, count, size); + if mi_unlikely(newp == NULL) { return errno; } + *op = newp; + return 0; +} + void* mi__expand(void* p, size_t newsize) mi_attr_noexcept { // Microsoft void* res = mi_expand(p, newsize); - if (res == NULL) errno = ENOMEM; + if (res == NULL) { errno = ENOMEM; } return res; } -mi_decl_restrict unsigned short* mi_wcsdup(const unsigned short* s) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict unsigned short* mi_wcsdup(const unsigned short* s) mi_attr_noexcept { if (s==NULL) return NULL; size_t len; for(len = 0; s[len] != 0; len++) { } @@ -108,7 +135,7 @@ mi_decl_restrict unsigned short* mi_wcsdup(const unsigned short* s) mi_attr_noex return p; } -mi_decl_restrict unsigned char* mi_mbsdup(const unsigned char* s) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict unsigned char* mi_mbsdup(const unsigned char* s) mi_attr_noexcept { return (unsigned char*)mi_strdup((const char*)s); } @@ -122,7 +149,7 @@ int mi_dupenv_s(char** buf, size_t* size, const char* name) mi_attr_noexcept { else { *buf = mi_strdup(p); if (*buf==NULL) return ENOMEM; - if (size != NULL) *size = strlen(p); + if (size != NULL) *size = _mi_strlen(p); } return 0; } @@ -148,10 +175,10 @@ int mi_wdupenv_s(unsigned short** buf, size_t* size, const unsigned short* name) #endif } -void* mi_aligned_offset_recalloc(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { // Microsoft +mi_decl_nodiscard void* mi_aligned_offset_recalloc(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { // Microsoft return mi_recalloc_aligned_at(p, newcount, size, alignment, offset); } -void* mi_aligned_recalloc(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { // Microsoft +mi_decl_nodiscard void* mi_aligned_recalloc(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { // Microsoft return mi_recalloc_aligned(p, newcount, size, alignment); } diff --git a/Source/mimalloc/src/alloc.c b/Source/mimalloc/src/alloc.c index 8acff7832..147e11094 100644 --- a/Source/mimalloc/src/alloc.c +++ b/Source/mimalloc/src/alloc.c @@ -1,15 +1,20 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2022, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE // for realpath() on Linux +#endif + #include "mimalloc.h" -#include "mimalloc-internal.h" -#include "mimalloc-atomic.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" +#include "mimalloc/prim.h" // _mi_prim_thread_id() -#include // memset, strlen -#include // malloc, exit +#include // memset, strlen (for mi_strdup) +#include // malloc, abort #define MI_IN_ALLOC_C #include "alloc-override.c" @@ -21,11 +26,11 @@ terms of the MIT license. A copy of the license can be found in the file // Fast allocation in a page: just pop from the free list. // Fall back to generic allocation only if the list is empty. -extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept { +extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept { mi_assert_internal(page->xblock_size==0||mi_page_block_size(page) >= size); mi_block_t* const block = page->free; - if (mi_unlikely(block == NULL)) { - return _mi_malloc_generic(heap, size); + if mi_unlikely(block == NULL) { + return _mi_malloc_generic(heap, size, zero, 0); } mi_assert_internal(block != NULL && _mi_ptr_page(block) == page); // pop from the free list @@ -33,15 +38,29 @@ extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t siz page->free = mi_block_next(page, block); mi_assert_internal(page->free == NULL || _mi_ptr_page(page->free) == page); -#if (MI_DEBUG>0) - if (!page->is_zero) { memset(block, MI_DEBUG_UNINIT, size); } + // allow use of the block internally + // note: when tracking we need to avoid ever touching the MI_PADDING since + // that is tracked by valgrind etc. as non-accessible (through the red-zone, see `mimalloc/track.h`) + mi_track_mem_undefined(block, mi_page_usable_block_size(page)); + + // zero the block? note: we need to zero the full block size (issue #63) + if mi_unlikely(zero) { + mi_assert_internal(page->xblock_size != 0); // do not call with zero'ing for huge blocks (see _mi_malloc_generic) + const size_t zsize = (page->is_zero ? sizeof(block->next) + MI_PADDING_SIZE : page->xblock_size); + _mi_memzero_aligned(block, zsize - MI_PADDING_SIZE); + } + +#if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN + if (!page->is_zero && !zero && !mi_page_is_huge(page)) { + memset(block, MI_DEBUG_UNINIT, mi_page_usable_block_size(page)); + } #elif (MI_SECURE!=0) - block->next = 0; // don't leak internal data + if (!zero) { block->next = 0; } // don't leak internal data #endif #if (MI_STAT>0) const size_t bsize = mi_page_usable_block_size(page); - if (bsize <= MI_LARGE_OBJ_SIZE_MAX) { + if (bsize <= MI_MEDIUM_OBJ_SIZE_MAX) { mi_heap_stat_increase(heap, normal, bsize); mi_heap_stat_counter_increase(heap, normal_count, 1); #if (MI_STAT>1) @@ -51,59 +70,72 @@ extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t siz } #endif -#if (MI_PADDING > 0) && defined(MI_ENCODE_FREELIST) +#if MI_PADDING // && !MI_TRACK_ENABLED mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + mi_page_usable_block_size(page)); ptrdiff_t delta = ((uint8_t*)padding - (uint8_t*)block - (size - MI_PADDING_SIZE)); + #if (MI_DEBUG>=2) mi_assert_internal(delta >= 0 && mi_page_usable_block_size(page) >= (size - MI_PADDING_SIZE + delta)); + #endif + mi_track_mem_defined(padding,sizeof(mi_padding_t)); // note: re-enable since mi_page_usable_block_size may set noaccess padding->canary = (uint32_t)(mi_ptr_encode(page,block,page->keys)); padding->delta = (uint32_t)(delta); - uint8_t* fill = (uint8_t*)padding - delta; - const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // set at most N initial padding bytes - for (size_t i = 0; i < maxpad; i++) { fill[i] = MI_DEBUG_PADDING; } + #if MI_PADDING_CHECK + if (!mi_page_is_huge(page)) { + uint8_t* fill = (uint8_t*)padding - delta; + const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // set at most N initial padding bytes + for (size_t i = 0; i < maxpad; i++) { fill[i] = MI_DEBUG_PADDING; } + } + #endif #endif return block; } -// allocate a small block -extern inline mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept { - mi_assert(heap!=NULL); - mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local +static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept { + mi_assert(heap != NULL); + #if MI_DEBUG + const uintptr_t tid = _mi_thread_id(); + mi_assert(heap->thread_id == 0 || heap->thread_id == tid); // heaps are thread local + #endif mi_assert(size <= MI_SMALL_SIZE_MAX); #if (MI_PADDING) - if (size == 0) { - size = sizeof(void*); - } + if (size == 0) { size = sizeof(void*); } #endif - mi_page_t* page = _mi_heap_get_free_small_page(heap,size + MI_PADDING_SIZE); - void* p = _mi_page_malloc(heap, page, size + MI_PADDING_SIZE); - mi_assert_internal(p==NULL || mi_usable_size(p) >= size); + mi_page_t* page = _mi_heap_get_free_small_page(heap, size + MI_PADDING_SIZE); + void* const p = _mi_page_malloc(heap, page, size + MI_PADDING_SIZE, zero); + mi_track_malloc(p,size,zero); #if MI_STAT>1 if (p != NULL) { - if (!mi_heap_is_initialized(heap)) { heap = mi_get_default_heap(); } + if (!mi_heap_is_initialized(heap)) { heap = mi_prim_get_default_heap(); } mi_heap_stat_increase(heap, malloc, mi_usable_size(p)); } #endif return p; } -extern inline mi_decl_restrict void* mi_malloc_small(size_t size) mi_attr_noexcept { - return mi_heap_malloc_small(mi_get_default_heap(), size); +// allocate a small block +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept { + return mi_heap_malloc_small_zero(heap, size, false); +} + +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc_small(size_t size) mi_attr_noexcept { + return mi_heap_malloc_small(mi_prim_get_default_heap(), size); } // The main allocation function -extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { - if (mi_likely(size <= MI_SMALL_SIZE_MAX)) { - return mi_heap_malloc_small(heap, size); +extern inline void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept { + if mi_likely(size <= MI_SMALL_SIZE_MAX) { + mi_assert_internal(huge_alignment == 0); + return mi_heap_malloc_small_zero(heap, size, zero); } else { mi_assert(heap!=NULL); - mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local - void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE); // note: size can overflow but it is detected in malloc_generic - mi_assert_internal(p == NULL || mi_usable_size(p) >= size); + mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local + void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE, zero, huge_alignment); // note: size can overflow but it is detected in malloc_generic + mi_track_malloc(p,size,zero); #if MI_STAT>1 if (p != NULL) { - if (!mi_heap_is_initialized(heap)) { heap = mi_get_default_heap(); } + if (!mi_heap_is_initialized(heap)) { heap = mi_prim_get_default_heap(); } mi_heap_stat_increase(heap, malloc, mi_usable_size(p)); } #endif @@ -111,52 +143,29 @@ extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size } } -extern inline mi_decl_restrict void* mi_malloc(size_t size) mi_attr_noexcept { - return mi_heap_malloc(mi_get_default_heap(), size); +extern inline void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept { + return _mi_heap_malloc_zero_ex(heap, size, zero, 0); } - -void _mi_block_zero_init(const mi_page_t* page, void* p, size_t size) { - // note: we need to initialize the whole usable block size to zero, not just the requested size, - // or the recalloc/rezalloc functions cannot safely expand in place (see issue #63) - UNUSED(size); - mi_assert_internal(p != NULL); - mi_assert_internal(mi_usable_size(p) >= size); // size can be zero - mi_assert_internal(_mi_ptr_page(p)==page); - if (page->is_zero && size > sizeof(mi_block_t)) { - // already zero initialized memory - ((mi_block_t*)p)->next = 0; // clear the free list pointer - mi_assert_expensive(mi_mem_is_zero(p, mi_usable_size(p))); - } - else { - // otherwise memset - memset(p, 0, mi_usable_size(p)); - } +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { + return _mi_heap_malloc_zero(heap, size, false); } -// zero initialized small block -mi_decl_restrict void* mi_zalloc_small(size_t size) mi_attr_noexcept { - void* p = mi_malloc_small(size); - if (p != NULL) { - _mi_block_zero_init(_mi_ptr_page(p), p, size); // todo: can we avoid getting the page again? - } - return p; +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc(size_t size) mi_attr_noexcept { + return mi_heap_malloc(mi_prim_get_default_heap(), size); } -void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) { - void* p = mi_heap_malloc(heap,size); - if (zero && p != NULL) { - _mi_block_zero_init(_mi_ptr_page(p),p,size); // todo: can we avoid getting the page again? - } - return p; +// zero initialized small block +mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_small(size_t size) mi_attr_noexcept { + return mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, true); } -extern inline mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { return _mi_heap_malloc_zero(heap, size, true); } -mi_decl_restrict void* mi_zalloc(size_t size) mi_attr_noexcept { - return mi_heap_zalloc(mi_get_default_heap(),size); +mi_decl_nodiscard mi_decl_restrict void* mi_zalloc(size_t size) mi_attr_noexcept { + return mi_heap_zalloc(mi_prim_get_default_heap(),size); } @@ -188,21 +197,24 @@ static mi_decl_noinline bool mi_check_is_double_freex(const mi_page_t* page, con return false; } +#define mi_track_page(page,access) { size_t psize; void* pstart = _mi_page_start(_mi_page_segment(page),page,&psize); mi_track_mem_##access( pstart, psize); } + static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block) { + bool is_double_free = false; mi_block_t* n = mi_block_nextx(page, block, page->keys); // pretend it is freed, and get the decoded first field if (((uintptr_t)n & (MI_INTPTR_SIZE-1))==0 && // quick check: aligned pointer? (n==NULL || mi_is_in_same_page(block, n))) // quick check: in same page or NULL? { // Suspicous: decoded value a in block is in the same page (or NULL) -- maybe a double free? // (continue in separate function to improve code generation) - return mi_check_is_double_freex(page, block); + is_double_free = mi_check_is_double_freex(page, block); } - return false; + return is_double_free; } #else static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block) { - UNUSED(page); - UNUSED(block); + MI_UNUSED(page); + MI_UNUSED(block); return false; } #endif @@ -211,12 +223,19 @@ static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block // Check for heap block overflow by setting up padding at the end of the block // --------------------------------------------------------------------------- -#if (MI_PADDING>0) && defined(MI_ENCODE_FREELIST) +#if MI_PADDING // && !MI_TRACK_ENABLED static bool mi_page_decode_padding(const mi_page_t* page, const mi_block_t* block, size_t* delta, size_t* bsize) { *bsize = mi_page_usable_block_size(page); const mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + *bsize); + mi_track_mem_defined(padding,sizeof(mi_padding_t)); *delta = padding->delta; - return ((uint32_t)mi_ptr_encode(page,block,page->keys) == padding->canary && *delta <= *bsize); + uint32_t canary = padding->canary; + uintptr_t keys[2]; + keys[0] = page->keys[0]; + keys[1] = page->keys[1]; + bool ok = ((uint32_t)mi_ptr_encode(page,block,keys) == canary && *delta <= *bsize); + mi_track_mem_noaccess(padding,sizeof(mi_padding_t)); + return ok; } // Return the exact usable size of a block. @@ -228,6 +247,40 @@ static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* bl return (ok ? bsize - delta : 0); } +// When a non-thread-local block is freed, it becomes part of the thread delayed free +// list that is freed later by the owning heap. If the exact usable size is too small to +// contain the pointer for the delayed list, then shrink the padding (by decreasing delta) +// so it will later not trigger an overflow error in `mi_free_block`. +void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size) { + size_t bsize; + size_t delta; + bool ok = mi_page_decode_padding(page, block, &delta, &bsize); + mi_assert_internal(ok); + if (!ok || (bsize - delta) >= min_size) return; // usually already enough space + mi_assert_internal(bsize >= min_size); + if (bsize < min_size) return; // should never happen + size_t new_delta = (bsize - min_size); + mi_assert_internal(new_delta < bsize); + mi_padding_t* padding = (mi_padding_t*)((uint8_t*)block + bsize); + mi_track_mem_defined(padding,sizeof(mi_padding_t)); + padding->delta = (uint32_t)new_delta; + mi_track_mem_noaccess(padding,sizeof(mi_padding_t)); +} +#else +static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block) { + MI_UNUSED(block); + return mi_page_usable_block_size(page); +} + +void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size) { + MI_UNUSED(page); + MI_UNUSED(block); + MI_UNUSED(min_size); +} +#endif + +#if MI_PADDING && MI_PADDING_CHECK + static bool mi_verify_padding(const mi_page_t* page, const mi_block_t* block, size_t* size, size_t* wrong) { size_t bsize; size_t delta; @@ -236,15 +289,20 @@ static bool mi_verify_padding(const mi_page_t* page, const mi_block_t* block, si if (!ok) return false; mi_assert_internal(bsize >= delta); *size = bsize - delta; - uint8_t* fill = (uint8_t*)block + bsize - delta; - const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // check at most the first N padding bytes - for (size_t i = 0; i < maxpad; i++) { - if (fill[i] != MI_DEBUG_PADDING) { - *wrong = bsize - delta + i; - return false; + if (!mi_page_is_huge(page)) { + uint8_t* fill = (uint8_t*)block + bsize - delta; + const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // check at most the first N padding bytes + mi_track_mem_defined(fill, maxpad); + for (size_t i = 0; i < maxpad; i++) { + if (fill[i] != MI_DEBUG_PADDING) { + *wrong = bsize - delta + i; + ok = false; + break; + } } + mi_track_mem_noaccess(fill, maxpad); } - return true; + return ok; } static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) { @@ -255,106 +313,99 @@ static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) { } } -// When a non-thread-local block is freed, it becomes part of the thread delayed free -// list that is freed later by the owning heap. If the exact usable size is too small to -// contain the pointer for the delayed list, then shrink the padding (by decreasing delta) -// so it will later not trigger an overflow error in `mi_free_block`. -static void mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size) { - size_t bsize; - size_t delta; - bool ok = mi_page_decode_padding(page, block, &delta, &bsize); - mi_assert_internal(ok); - if (!ok || (bsize - delta) >= min_size) return; // usually already enough space - mi_assert_internal(bsize >= min_size); - if (bsize < min_size) return; // should never happen - size_t new_delta = (bsize - min_size); - mi_assert_internal(new_delta < bsize); - mi_padding_t* padding = (mi_padding_t*)((uint8_t*)block + bsize); - padding->delta = (uint32_t)new_delta; -} #else -static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) { - UNUSED(page); - UNUSED(block); -} -static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block) { - UNUSED(block); - return mi_page_usable_block_size(page); +static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) { + MI_UNUSED(page); + MI_UNUSED(block); } -static void mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size) { - UNUSED(page); - UNUSED(block); - UNUSED(min_size); -} #endif // only maintain stats for smaller objects if requested #if (MI_STAT>0) static void mi_stat_free(const mi_page_t* page, const mi_block_t* block) { -#if (MI_STAT < 2) - UNUSED(block); -#endif + #if (MI_STAT < 2) + MI_UNUSED(block); + #endif mi_heap_t* const heap = mi_heap_get_default(); - const size_t bsize = mi_page_usable_block_size(page); -#if (MI_STAT>1) + const size_t bsize = mi_page_usable_block_size(page); + #if (MI_STAT>1) const size_t usize = mi_page_usable_size_of(page, block); mi_heap_stat_decrease(heap, malloc, usize); -#endif - if (bsize <= MI_LARGE_OBJ_SIZE_MAX) { + #endif + if (bsize <= MI_MEDIUM_OBJ_SIZE_MAX) { mi_heap_stat_decrease(heap, normal, bsize); -#if (MI_STAT > 1) + #if (MI_STAT > 1) mi_heap_stat_decrease(heap, normal_bins[_mi_bin(bsize)], 1); -#endif + #endif + } + else if (bsize <= MI_LARGE_OBJ_SIZE_MAX) { + mi_heap_stat_decrease(heap, large, bsize); } + else { + mi_heap_stat_decrease(heap, huge, bsize); + } } #else static void mi_stat_free(const mi_page_t* page, const mi_block_t* block) { - UNUSED(page); UNUSED(block); + MI_UNUSED(page); MI_UNUSED(block); } #endif +#if MI_HUGE_PAGE_ABANDON #if (MI_STAT>0) // maintain stats for huge objects static void mi_stat_huge_free(const mi_page_t* page) { mi_heap_t* const heap = mi_heap_get_default(); const size_t bsize = mi_page_block_size(page); // to match stats in `page.c:mi_page_huge_alloc` - if (bsize <= MI_HUGE_OBJ_SIZE_MAX) { - mi_heap_stat_decrease(heap, huge, bsize); + if (bsize <= MI_LARGE_OBJ_SIZE_MAX) { + mi_heap_stat_decrease(heap, large, bsize); } else { - mi_heap_stat_decrease(heap, giant, bsize); + mi_heap_stat_decrease(heap, huge, bsize); } } #else static void mi_stat_huge_free(const mi_page_t* page) { - UNUSED(page); + MI_UNUSED(page); } #endif +#endif // ------------------------------------------------------ // Free // ------------------------------------------------------ -// multi-threaded free +// multi-threaded free (or free in huge block if compiled with MI_HUGE_PAGE_ABANDON) static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* block) { // The padding check may access the non-thread-owned page for the key values. // that is safe as these are constant and the page won't be freed (as the block is not freed yet). mi_check_padding(page, block); - mi_padding_shrink(page, block, sizeof(mi_block_t)); // for small size, ensure we can fit the delayed thread pointers without triggering overflow detection - #if (MI_DEBUG!=0) - memset(block, MI_DEBUG_FREED, mi_usable_size(block)); - #endif - + _mi_padding_shrink(page, block, sizeof(mi_block_t)); // for small size, ensure we can fit the delayed thread pointers without triggering overflow detection + // huge page segments are always abandoned and can be freed immediately - mi_segment_t* const segment = _mi_page_segment(page); - if (segment->page_kind==MI_PAGE_HUGE) { + mi_segment_t* segment = _mi_page_segment(page); + if (segment->kind == MI_SEGMENT_HUGE) { + #if MI_HUGE_PAGE_ABANDON + // huge page segments are always abandoned and can be freed immediately mi_stat_huge_free(page); _mi_segment_huge_page_free(segment, page, block); return; + #else + // huge pages are special as they occupy the entire segment + // as these are large we reset the memory occupied by the page so it is available to other threads + // (as the owning thread needs to actually free the memory later). + _mi_segment_huge_page_reset(segment, page, block); + #endif + } + + #if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN // note: when tracking, cannot use mi_usable_size with multi-threading + if (segment->kind != MI_SEGMENT_HUGE) { // not for huge segments as we just reset the content + memset(block, MI_DEBUG_FREED, mi_usable_size(block)); } + #endif // Try to put the block on either the page-local thread free list, or the heap delayed free list. mi_thread_free_t tfreex; @@ -362,7 +413,7 @@ static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* bloc mi_thread_free_t tfree = mi_atomic_load_relaxed(&page->xthread_free); do { use_delayed = (mi_tf_delayed(tfree) == MI_USE_DELAYED_FREE); - if (mi_unlikely(use_delayed)) { + if mi_unlikely(use_delayed) { // unlikely: this only happens on the first concurrent free in a page that is in the full list tfreex = mi_tf_set_delayed(tfree,MI_DELAYED_FREEING); } @@ -373,7 +424,7 @@ static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* bloc } } while (!mi_atomic_cas_weak_release(&page->xthread_free, &tfree, tfreex)); - if (mi_unlikely(use_delayed)) { + if mi_unlikely(use_delayed) { // racy read on `heap`, but ok because MI_DELAYED_FREEING is set (see `mi_heap_delete` and `mi_heap_collect_abandon`) mi_heap_t* const heap = (mi_heap_t*)(mi_atomic_load_acquire(&page->xheap)); //mi_page_heap(page); mi_assert_internal(heap != NULL); @@ -399,20 +450,23 @@ static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* bloc static inline void _mi_free_block(mi_page_t* page, bool local, mi_block_t* block) { // and push it on the free list - if (mi_likely(local)) { + //const size_t bsize = mi_page_block_size(page); + if mi_likely(local) { // owning thread can free a block directly - if (mi_unlikely(mi_check_is_double_free(page, block))) return; + if mi_unlikely(mi_check_is_double_free(page, block)) return; mi_check_padding(page, block); - #if (MI_DEBUG!=0) - memset(block, MI_DEBUG_FREED, mi_page_block_size(page)); + #if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN + if (!mi_page_is_huge(page)) { // huge page content may be already decommitted + memset(block, MI_DEBUG_FREED, mi_page_block_size(page)); + } #endif mi_block_set_next(page, block, page->local_free); page->local_free = block; page->used--; - if (mi_unlikely(mi_page_all_free(page))) { + if mi_unlikely(mi_page_all_free(page)) { _mi_page_retire(page); } - else if (mi_unlikely(mi_page_is_in_full(page))) { + else if mi_unlikely(mi_page_is_in_full(page)) { _mi_page_unfull(page); } } @@ -431,78 +485,94 @@ mi_block_t* _mi_page_ptr_unalign(const mi_segment_t* segment, const mi_page_t* p } -static void mi_decl_noinline mi_free_generic(const mi_segment_t* segment, bool local, void* p) { - mi_page_t* const page = _mi_segment_page_of(segment, p); +void mi_decl_noinline _mi_free_generic(const mi_segment_t* segment, mi_page_t* page, bool is_local, void* p) mi_attr_noexcept { mi_block_t* const block = (mi_page_has_aligned(page) ? _mi_page_ptr_unalign(segment, page, p) : (mi_block_t*)p); - mi_stat_free(page, block); - _mi_free_block(page, local, block); + mi_stat_free(page, block); // stat_free may access the padding + mi_track_free_size(block, mi_page_usable_size_of(page,block)); + _mi_free_block(page, is_local, block); } // Get the segment data belonging to a pointer // This is just a single `and` in assembly but does further checks in debug mode // (and secure mode) if this was a valid pointer. -static inline mi_segment_t* mi_checked_ptr_segment(const void* p, const char* msg) +static inline mi_segment_t* mi_checked_ptr_segment(const void* p, const char* msg) { - UNUSED(msg); + MI_UNUSED(msg); + mi_assert(p != NULL); + #if (MI_DEBUG>0) - if (mi_unlikely(((uintptr_t)p & (MI_INTPTR_SIZE - 1)) != 0)) { + if mi_unlikely(((uintptr_t)p & (MI_INTPTR_SIZE - 1)) != 0) { _mi_error_message(EINVAL, "%s: invalid (unaligned) pointer: %p\n", msg, p); return NULL; } #endif mi_segment_t* const segment = _mi_ptr_segment(p); - if (mi_unlikely(segment == NULL)) return NULL; // checks also for (p==NULL) + mi_assert_internal(segment != NULL); #if (MI_DEBUG>0) - if (mi_unlikely(!mi_is_in_heap_region(p))) { - _mi_warning_message("%s: pointer might not point to a valid heap region: %p\n" - "(this may still be a valid very large allocation (over 64MiB))\n", msg, p); - if (mi_likely(_mi_ptr_cookie(segment) == segment->cookie)) { - _mi_warning_message("(yes, the previous pointer %p was valid after all)\n", p); + if mi_unlikely(!mi_is_in_heap_region(p)) { + #if (MI_INTPTR_SIZE == 8 && defined(__linux__)) + if (((uintptr_t)p >> 40) != 0x7F) { // linux tends to align large blocks above 0x7F000000000 (issue #640) + #else + { + #endif + _mi_warning_message("%s: pointer might not point to a valid heap region: %p\n" + "(this may still be a valid very large allocation (over 64MiB))\n", msg, p); + if mi_likely(_mi_ptr_cookie(segment) == segment->cookie) { + _mi_warning_message("(yes, the previous pointer %p was valid after all)\n", p); + } } } #endif #if (MI_DEBUG>0 || MI_SECURE>=4) - if (mi_unlikely(_mi_ptr_cookie(segment) != segment->cookie)) { - _mi_error_message(EINVAL, "%s: pointer does not point to a valid heap space: %p\n", p); + if mi_unlikely(_mi_ptr_cookie(segment) != segment->cookie) { + _mi_error_message(EINVAL, "%s: pointer does not point to a valid heap space: %p\n", msg, p); + return NULL; } #endif + return segment; } - // Free a block +// fast path written carefully to prevent spilling on the stack void mi_free(void* p) mi_attr_noexcept { - const mi_segment_t* const segment = mi_checked_ptr_segment(p,"mi_free"); - if (mi_unlikely(segment == NULL)) return; - - const uintptr_t tid = _mi_thread_id(); - mi_page_t* const page = _mi_segment_page_of(segment, p); - mi_block_t* const block = (mi_block_t*)p; - - if (mi_likely(tid == segment->thread_id && page->flags.full_aligned == 0)) { // the thread id matches and it is not a full page, nor has aligned blocks - // local, and not full or aligned - if (mi_unlikely(mi_check_is_double_free(page,block))) return; - mi_check_padding(page, block); - mi_stat_free(page, block); - #if (MI_DEBUG!=0) - memset(block, MI_DEBUG_FREED, mi_page_block_size(page)); - #endif - mi_block_set_next(page, block, page->local_free); - page->local_free = block; - if (mi_unlikely(--page->used == 0)) { // using this expression generates better code than: page->used--; if (mi_page_all_free(page)) - _mi_page_retire(page); + if mi_unlikely(p == NULL) return; + mi_segment_t* const segment = mi_checked_ptr_segment(p,"mi_free"); + const bool is_local= (_mi_prim_thread_id() == mi_atomic_load_relaxed(&segment->thread_id)); + mi_page_t* const page = _mi_segment_page_of(segment, p); + + if mi_likely(is_local) { // thread-local free? + if mi_likely(page->flags.full_aligned == 0) // and it is not a full page (full pages need to move from the full bin), nor has aligned blocks (aligned blocks need to be unaligned) + { + mi_block_t* const block = (mi_block_t*)p; + if mi_unlikely(mi_check_is_double_free(page, block)) return; + mi_check_padding(page, block); + mi_stat_free(page, block); + #if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN + memset(block, MI_DEBUG_FREED, mi_page_block_size(page)); + #endif + mi_track_free_size(p, mi_page_usable_size_of(page,block)); // faster then mi_usable_size as we already know the page and that p is unaligned + mi_block_set_next(page, block, page->local_free); + page->local_free = block; + if mi_unlikely(--page->used == 0) { // using this expression generates better code than: page->used--; if (mi_page_all_free(page)) + _mi_page_retire(page); + } + } + else { + // page is full or contains (inner) aligned blocks; use generic path + _mi_free_generic(segment, page, true, p); } } else { - // non-local, aligned blocks, or a full page; use the more generic path - // note: recalc page in generic to improve code generation - mi_free_generic(segment, tid == segment->thread_id, p); + // not thread-local; use generic path + _mi_free_generic(segment, page, false, p); } } +// return true if successful bool _mi_free_delayed_block(mi_block_t* block) { // get segment and page const mi_segment_t* const segment = _mi_ptr_segment(block); @@ -515,7 +585,9 @@ bool _mi_free_delayed_block(mi_block_t* block) { // some blocks may end up in the page `thread_free` list with no blocks in the // heap `thread_delayed_free` list which may cause the page to be never freed! // (it would only be freed if we happen to scan it in `mi_page_queue_find_free_ex`) - _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, false /* dont overwrite never delayed */); + if (!_mi_page_try_use_delayed_free(page, MI_USE_DELAYED_FREE, false /* dont overwrite never delayed */)) { + return false; + } // collect all other non-local frees to ensure up-to-date `used` count _mi_page_free_collect(page, false); @@ -526,120 +598,125 @@ bool _mi_free_delayed_block(mi_block_t* block) { } // Bytes available in a block -static size_t _mi_usable_size(const void* p, const char* msg) mi_attr_noexcept { - const mi_segment_t* const segment = mi_checked_ptr_segment(p,msg); - if (segment==NULL) return 0; +mi_decl_noinline static size_t mi_page_usable_aligned_size_of(const mi_segment_t* segment, const mi_page_t* page, const void* p) mi_attr_noexcept { + const mi_block_t* block = _mi_page_ptr_unalign(segment, page, p); + const size_t size = mi_page_usable_size_of(page, block); + const ptrdiff_t adjust = (uint8_t*)p - (uint8_t*)block; + mi_assert_internal(adjust >= 0 && (size_t)adjust <= size); + return (size - adjust); +} + +static inline size_t _mi_usable_size(const void* p, const char* msg) mi_attr_noexcept { + if (p == NULL) return 0; + const mi_segment_t* const segment = mi_checked_ptr_segment(p, msg); const mi_page_t* const page = _mi_segment_page_of(segment, p); - const mi_block_t* block = (const mi_block_t*)p; - if (mi_unlikely(mi_page_has_aligned(page))) { - block = _mi_page_ptr_unalign(segment, page, p); - size_t size = mi_page_usable_size_of(page, block); - ptrdiff_t const adjust = (uint8_t*)p - (uint8_t*)block; - mi_assert_internal(adjust >= 0 && (size_t)adjust <= size); - return (size - adjust); + if mi_likely(!mi_page_has_aligned(page)) { + const mi_block_t* block = (const mi_block_t*)p; + return mi_page_usable_size_of(page, block); } else { - return mi_page_usable_size_of(page, block); + // split out to separate routine for improved code generation + return mi_page_usable_aligned_size_of(segment, page, p); } } -size_t mi_usable_size(const void* p) mi_attr_noexcept { +mi_decl_nodiscard size_t mi_usable_size(const void* p) mi_attr_noexcept { return _mi_usable_size(p, "mi_usable_size"); } -// ------------------------------------------------------ -// ensure explicit external inline definitions are emitted! -// ------------------------------------------------------ - -#ifdef __cplusplus -void* _mi_externs[] = { - (void*)&_mi_page_malloc, - (void*)&mi_malloc, - (void*)&mi_malloc_small, - (void*)&mi_zalloc_small, - (void*)&mi_heap_malloc, - (void*)&mi_heap_zalloc, - (void*)&mi_heap_malloc_small -}; -#endif - - // ------------------------------------------------------ // Allocation extensions // ------------------------------------------------------ void mi_free_size(void* p, size_t size) mi_attr_noexcept { - UNUSED_RELEASE(size); + MI_UNUSED_RELEASE(size); mi_assert(p == NULL || size <= _mi_usable_size(p,"mi_free_size")); mi_free(p); } void mi_free_size_aligned(void* p, size_t size, size_t alignment) mi_attr_noexcept { - UNUSED_RELEASE(alignment); + MI_UNUSED_RELEASE(alignment); mi_assert(((uintptr_t)p % alignment) == 0); mi_free_size(p,size); } void mi_free_aligned(void* p, size_t alignment) mi_attr_noexcept { - UNUSED_RELEASE(alignment); + MI_UNUSED_RELEASE(alignment); mi_assert(((uintptr_t)p % alignment) == 0); mi_free(p); } -extern inline mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count,size,&total)) return NULL; return mi_heap_zalloc(heap,total); } -mi_decl_restrict void* mi_calloc(size_t count, size_t size) mi_attr_noexcept { - return mi_heap_calloc(mi_get_default_heap(),count,size); +mi_decl_nodiscard mi_decl_restrict void* mi_calloc(size_t count, size_t size) mi_attr_noexcept { + return mi_heap_calloc(mi_prim_get_default_heap(),count,size); } // Uninitialized `calloc` -extern mi_decl_restrict void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard extern mi_decl_restrict void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; return mi_heap_malloc(heap, total); } -mi_decl_restrict void* mi_mallocn(size_t count, size_t size) mi_attr_noexcept { - return mi_heap_mallocn(mi_get_default_heap(),count,size); +mi_decl_nodiscard mi_decl_restrict void* mi_mallocn(size_t count, size_t size) mi_attr_noexcept { + return mi_heap_mallocn(mi_prim_get_default_heap(),count,size); } -// Expand in place or fail +// Expand (or shrink) in place (or fail) void* mi_expand(void* p, size_t newsize) mi_attr_noexcept { + #if MI_PADDING + // we do not shrink/expand with padding enabled + MI_UNUSED(p); MI_UNUSED(newsize); + return NULL; + #else if (p == NULL) return NULL; - size_t size = _mi_usable_size(p,"mi_expand"); + const size_t size = _mi_usable_size(p,"mi_expand"); if (newsize > size) return NULL; return p; // it fits + #endif } -void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) { - if (p == NULL) return _mi_heap_malloc_zero(heap,newsize,zero); - size_t size = _mi_usable_size(p,"mi_realloc"); - if (newsize <= size && newsize >= (size / 2)) { +void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) mi_attr_noexcept { + // if p == NULL then behave as malloc. + // else if size == 0 then reallocate to a zero-sized block (and don't return NULL, just as mi_malloc(0)). + // (this means that returning NULL always indicates an error, and `p` will not have been freed in that case.) + const size_t size = _mi_usable_size(p,"mi_realloc"); // also works if p == NULL (with size 0) + if mi_unlikely(newsize <= size && newsize >= (size / 2) && newsize > 0) { // note: newsize must be > 0 or otherwise we return NULL for realloc(NULL,0) + mi_assert_internal(p!=NULL); + // todo: do not track as the usable size is still the same in the free; adjust potential padding? + // mi_track_resize(p,size,newsize) return p; // reallocation still fits and not more than 50% waste } void* newp = mi_heap_malloc(heap,newsize); - if (mi_likely(newp != NULL)) { + if mi_likely(newp != NULL) { if (zero && newsize > size) { // also set last word in the previous allocation to zero to ensure any padding is zero-initialized - size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); + const size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); memset((uint8_t*)newp + start, 0, newsize - start); } - _mi_memcpy_aligned(newp, p, (newsize > size ? size : newsize)); - mi_free(p); // only free if successful + if mi_likely(p != NULL) { + if mi_likely(_mi_is_aligned(p, sizeof(uintptr_t))) { // a client may pass in an arbitrary pointer `p`.. + const size_t copysize = (newsize > size ? size : newsize); + mi_track_mem_defined(p,copysize); // _mi_useable_size may be too large for byte precise memory tracking.. + _mi_memcpy_aligned(newp, p, copysize); + } + mi_free(p); // only free the original pointer if successful + } } return newp; } -void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { return _mi_heap_realloc_zero(heap, p, newsize, false); } -void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; return mi_heap_realloc(heap, p, total); @@ -647,42 +724,42 @@ void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_a // Reallocate but free `p` on errors -void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { void* newp = mi_heap_realloc(heap, p, newsize); if (newp==NULL && p!=NULL) mi_free(p); return newp; } -void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { return _mi_heap_realloc_zero(heap, p, newsize, true); } -void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; return mi_heap_rezalloc(heap, p, total); } -void* mi_realloc(void* p, size_t newsize) mi_attr_noexcept { - return mi_heap_realloc(mi_get_default_heap(),p,newsize); +mi_decl_nodiscard void* mi_realloc(void* p, size_t newsize) mi_attr_noexcept { + return mi_heap_realloc(mi_prim_get_default_heap(),p,newsize); } -void* mi_reallocn(void* p, size_t count, size_t size) mi_attr_noexcept { - return mi_heap_reallocn(mi_get_default_heap(),p,count,size); +mi_decl_nodiscard void* mi_reallocn(void* p, size_t count, size_t size) mi_attr_noexcept { + return mi_heap_reallocn(mi_prim_get_default_heap(),p,count,size); } // Reallocate but free `p` on errors -void* mi_reallocf(void* p, size_t newsize) mi_attr_noexcept { - return mi_heap_reallocf(mi_get_default_heap(),p,newsize); +mi_decl_nodiscard void* mi_reallocf(void* p, size_t newsize) mi_attr_noexcept { + return mi_heap_reallocf(mi_prim_get_default_heap(),p,newsize); } -void* mi_rezalloc(void* p, size_t newsize) mi_attr_noexcept { - return mi_heap_rezalloc(mi_get_default_heap(), p, newsize); +mi_decl_nodiscard void* mi_rezalloc(void* p, size_t newsize) mi_attr_noexcept { + return mi_heap_rezalloc(mi_prim_get_default_heap(), p, newsize); } -void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept { - return mi_heap_recalloc(mi_get_default_heap(), p, count, size); +mi_decl_nodiscard void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept { + return mi_heap_recalloc(mi_prim_get_default_heap(), p, count, size); } @@ -692,20 +769,22 @@ void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept { // ------------------------------------------------------ // `strdup` using mi_malloc -mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_noexcept { if (s == NULL) return NULL; size_t n = strlen(s); char* t = (char*)mi_heap_malloc(heap,n+1); - if (t != NULL) _mi_memcpy(t, s, n + 1); + if (t == NULL) return NULL; + _mi_memcpy(t, s, n); + t[n] = 0; return t; } -mi_decl_restrict char* mi_strdup(const char* s) mi_attr_noexcept { - return mi_heap_strdup(mi_get_default_heap(), s); +mi_decl_nodiscard mi_decl_restrict char* mi_strdup(const char* s) mi_attr_noexcept { + return mi_heap_strdup(mi_prim_get_default_heap(), s); } // `strndup` using mi_malloc -mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) mi_attr_noexcept { if (s == NULL) return NULL; const char* end = (const char*)memchr(s, 0, n); // find end of string in the first `n` characters (returns NULL if not found) const size_t m = (end != NULL ? (size_t)(end - s) : n); // `m` is the minimum of `n` or the end-of-string @@ -717,8 +796,8 @@ mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) return t; } -mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept { - return mi_heap_strndup(mi_get_default_heap(),s,n); +mi_decl_nodiscard mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept { + return mi_heap_strndup(mi_prim_get_default_heap(),s,n); } #ifndef __wasi__ @@ -728,7 +807,7 @@ mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept { #define PATH_MAX MAX_PATH #endif #include -mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept { // todo: use GetFullPathNameW to allow longer file names char buf[PATH_MAX]; DWORD res = GetFullPathNameA(fname, PATH_MAX, (resolved_name == NULL ? buf : resolved_name), NULL); @@ -746,8 +825,9 @@ mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char } } #else +/* #include // pathconf -static size_t mi_path_max() { +static size_t mi_path_max(void) { static size_t path_max = 0; if (path_max <= 0) { long m = pathconf("/",_PC_PATH_MAX); @@ -757,25 +837,36 @@ static size_t mi_path_max() { } return path_max; } - +*/ char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept { if (resolved_name != NULL) { return realpath(fname,resolved_name); } else { - size_t n = mi_path_max(); + char* rname = realpath(fname, NULL); + if (rname == NULL) return NULL; + char* result = mi_heap_strdup(heap, rname); + free(rname); // use regular free! (which may be redirected to our free but that's ok) + return result; + } + /* + const size_t n = mi_path_max(); char* buf = (char*)mi_malloc(n+1); - if (buf==NULL) return NULL; + if (buf == NULL) { + errno = ENOMEM; + return NULL; + } char* rname = realpath(fname,buf); char* result = mi_heap_strndup(heap,rname,n); // ok if `rname==NULL` mi_free(buf); return result; } + */ } #endif -mi_decl_restrict char* mi_realpath(const char* fname, char* resolved_name) mi_attr_noexcept { - return mi_heap_realpath(mi_get_default_heap(),fname,resolved_name); +mi_decl_nodiscard mi_decl_restrict char* mi_realpath(const char* fname, char* resolved_name) mi_attr_noexcept { + return mi_heap_realpath(mi_prim_get_default_heap(),fname,resolved_name); } #endif @@ -796,9 +887,12 @@ static bool mi_try_new_handler(bool nothrow) { #else std::new_handler h = std::set_new_handler(); std::set_new_handler(h); - #endif + #endif if (h==NULL) { - if (!nothrow) throw std::bad_alloc(); + _mi_error_message(ENOMEM, "out of memory in 'new'"); + if (!nothrow) { + throw std::bad_alloc(); + } return false; } else { @@ -807,13 +901,13 @@ static bool mi_try_new_handler(bool nothrow) { } } #else -typedef void (*std_new_handler_t)(); +typedef void (*std_new_handler_t)(void); -#if (defined(__GNUC__) || defined(__clang__)) -std_new_handler_t __attribute((weak)) _ZSt15get_new_handlerv() { +#if (defined(__GNUC__) || (defined(__clang__) && !defined(_MSC_VER))) // exclude clang-cl, see issue #631 +std_new_handler_t __attribute__((weak)) _ZSt15get_new_handlerv(void) { return NULL; } -static std_new_handler_t mi_get_new_handler() { +static std_new_handler_t mi_get_new_handler(void) { return _ZSt15get_new_handlerv(); } #else @@ -826,7 +920,10 @@ static std_new_handler_t mi_get_new_handler() { static bool mi_try_new_handler(bool nothrow) { std_new_handler_t h = mi_get_new_handler(); if (h==NULL) { - if (!nothrow) exit(ENOMEM); // cannot throw in plain C, use exit as we are out of memory anyway. + _mi_error_message(ENOMEM, "out of memory in 'new'"); + if (!nothrow) { + abort(); // cannot throw in plain C, use abort + } return false; } else { @@ -836,27 +933,53 @@ static bool mi_try_new_handler(bool nothrow) { } #endif -static mi_decl_noinline void* mi_try_new(size_t size, bool nothrow ) { +mi_decl_export mi_decl_noinline void* mi_heap_try_new(mi_heap_t* heap, size_t size, bool nothrow ) { void* p = NULL; while(p == NULL && mi_try_new_handler(nothrow)) { - p = mi_malloc(size); + p = mi_heap_malloc(heap,size); } return p; } -mi_decl_restrict void* mi_new(size_t size) { - void* p = mi_malloc(size); - if (mi_unlikely(p == NULL)) return mi_try_new(size,false); +static mi_decl_noinline void* mi_try_new(size_t size, bool nothrow) { + return mi_heap_try_new(mi_prim_get_default_heap(), size, nothrow); +} + + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_alloc_new(mi_heap_t* heap, size_t size) { + void* p = mi_heap_malloc(heap,size); + if mi_unlikely(p == NULL) return mi_heap_try_new(heap, size, false); return p; } -mi_decl_restrict void* mi_new_nothrow(size_t size) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_new(size_t size) { + return mi_heap_alloc_new(mi_prim_get_default_heap(), size); +} + + +mi_decl_nodiscard mi_decl_restrict void* mi_heap_alloc_new_n(mi_heap_t* heap, size_t count, size_t size) { + size_t total; + if mi_unlikely(mi_count_size_overflow(count, size, &total)) { + mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc + return NULL; + } + else { + return mi_heap_alloc_new(heap,total); + } +} + +mi_decl_nodiscard mi_decl_restrict void* mi_new_n(size_t count, size_t size) { + return mi_heap_alloc_new_n(mi_prim_get_default_heap(), size, count); +} + + +mi_decl_nodiscard mi_decl_restrict void* mi_new_nothrow(size_t size) mi_attr_noexcept { void* p = mi_malloc(size); - if (mi_unlikely(p == NULL)) return mi_try_new(size, true); + if mi_unlikely(p == NULL) return mi_try_new(size, true); return p; } -mi_decl_restrict void* mi_new_aligned(size_t size, size_t alignment) { +mi_decl_nodiscard mi_decl_restrict void* mi_new_aligned(size_t size, size_t alignment) { void* p; do { p = mi_malloc_aligned(size, alignment); @@ -865,7 +988,7 @@ mi_decl_restrict void* mi_new_aligned(size_t size, size_t alignment) { return p; } -mi_decl_restrict void* mi_new_aligned_nothrow(size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_new_aligned_nothrow(size_t size, size_t alignment) mi_attr_noexcept { void* p; do { p = mi_malloc_aligned(size, alignment); @@ -874,18 +997,7 @@ mi_decl_restrict void* mi_new_aligned_nothrow(size_t size, size_t alignment) mi_ return p; } -mi_decl_restrict void* mi_new_n(size_t count, size_t size) { - size_t total; - if (mi_unlikely(mi_count_size_overflow(count, size, &total))) { - mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc - return NULL; - } - else { - return mi_new(total); - } -} - -void* mi_new_realloc(void* p, size_t newsize) { +mi_decl_nodiscard void* mi_new_realloc(void* p, size_t newsize) { void* q; do { q = mi_realloc(p, newsize); @@ -893,9 +1005,9 @@ void* mi_new_realloc(void* p, size_t newsize) { return q; } -void* mi_new_reallocn(void* p, size_t newcount, size_t size) { +mi_decl_nodiscard void* mi_new_reallocn(void* p, size_t newcount, size_t size) { size_t total; - if (mi_unlikely(mi_count_size_overflow(newcount, size, &total))) { + if mi_unlikely(mi_count_size_overflow(newcount, size, &total)) { mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc return NULL; } @@ -903,3 +1015,23 @@ void* mi_new_reallocn(void* p, size_t newcount, size_t size) { return mi_new_realloc(p, total); } } + +// ------------------------------------------------------ +// ensure explicit external inline definitions are emitted! +// ------------------------------------------------------ + +#ifdef __cplusplus +void* _mi_externs[] = { + (void*)&_mi_page_malloc, + (void*)&_mi_heap_malloc_zero, + (void*)&_mi_heap_malloc_zero_ex, + (void*)&mi_malloc, + (void*)&mi_malloc_small, + (void*)&mi_zalloc_small, + (void*)&mi_heap_malloc, + (void*)&mi_heap_zalloc, + (void*)&mi_heap_malloc_small + // (void*)&mi_heap_alloc_new, + // (void*)&mi_heap_alloc_new_n +}; +#endif diff --git a/Source/mimalloc/src/arena.c b/Source/mimalloc/src/arena.c index 0e6615a42..35cbcde6a 100644 --- a/Source/mimalloc/src/arena.c +++ b/Source/mimalloc/src/arena.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2019-2021, Microsoft Research, Daan Leijen +Copyright (c) 2019-2022, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -7,93 +7,117 @@ terms of the MIT license. A copy of the license can be found in the file /* ---------------------------------------------------------------------------- "Arenas" are fixed area's of OS memory from which we can allocate -large blocks (>= MI_ARENA_BLOCK_SIZE, 32MiB). +large blocks (>= MI_ARENA_MIN_BLOCK_SIZE, 4MiB). In contrast to the rest of mimalloc, the arenas are shared between threads and need to be accessed using atomic operations. -Currently arenas are only used to for huge OS page (1GiB) reservations, -otherwise it delegates to direct allocation from the OS. -In the future, we can expose an API to manually add more kinds of arenas -which is sometimes needed for embedded devices or shared memory for example. -(We can also employ this with WASI or `sbrk` systems to reserve large arenas - on demand and be able to reuse them efficiently). - -The arena allocation needs to be thread safe and we use an atomic -bitmap to allocate. The current implementation of the bitmap can -only do this within a field (`uintptr_t`) so we can allocate at most -blocks of 2GiB (64*32MiB) and no object can cross the boundary. This -can lead to fragmentation but fortunately most objects will be regions -of 256MiB in practice. +Arenas are used to for huge OS page (1GiB) reservations or for reserving +OS memory upfront which can be improve performance or is sometimes needed +on embedded devices. We can also employ this with WASI or `sbrk` systems +to reserve large arenas upfront and be able to reuse the memory more effectively. + +The arena allocation needs to be thread safe and we use an atomic bitmap to allocate. -----------------------------------------------------------------------------*/ #include "mimalloc.h" -#include "mimalloc-internal.h" -#include "mimalloc-atomic.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" #include // memset #include // ENOMEM #include "bitmap.h" // atomic bitmap - -// os.c -void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool* large, mi_stats_t* stats); -void _mi_os_free_ex(void* p, size_t size, bool was_committed, mi_stats_t* stats); -void _mi_os_free(void* p, size_t size, mi_stats_t* stats); - -void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_secs, size_t* pages_reserved, size_t* psize); -void _mi_os_free_huge_pages(void* p, size_t size, mi_stats_t* stats); - -bool _mi_os_commit(void* p, size_t size, bool* is_zero, mi_stats_t* stats); -bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats); - /* ----------------------------------------------------------- Arena allocation ----------------------------------------------------------- */ -#define MI_SEGMENT_ALIGN MI_SEGMENT_SIZE -#define MI_ARENA_BLOCK_SIZE (4*MI_SEGMENT_ALIGN) // 32MiB -#define MI_ARENA_MIN_OBJ_SIZE (MI_ARENA_BLOCK_SIZE/2) // 16MiB -#define MI_MAX_ARENAS (64) // not more than 256 (since we use 8 bits in the memid) +// Block info: bit 0 contains the `in_use` bit, the upper bits the +// size in count of arena blocks. +typedef uintptr_t mi_block_info_t; +#define MI_ARENA_BLOCK_SIZE (MI_SEGMENT_SIZE) // 64MiB (must be at least MI_SEGMENT_ALIGN) +#define MI_ARENA_MIN_OBJ_SIZE (MI_ARENA_BLOCK_SIZE/2) // 32MiB +#define MI_MAX_ARENAS (64) // not more than 126 (since we use 7 bits in the memid and an arena index + 1) // A memory arena descriptor typedef struct mi_arena_s { + mi_arena_id_t id; // arena id; 0 for non-specific + bool exclusive; // only allow allocations if specifically for this arena _Atomic(uint8_t*) start; // the start of the memory area size_t block_count; // size of the area in arena blocks (of `MI_ARENA_BLOCK_SIZE`) size_t field_count; // number of bitmap fields (where `field_count * MI_BITMAP_FIELD_BITS >= block_count`) int numa_node; // associated NUMA node bool is_zero_init; // is the arena zero initialized? - bool is_committed; // is the memory fully committed? (if so, block_committed == NULL) + bool allow_decommit; // is decommit allowed? if true, is_large should be false and blocks_committed != NULL bool is_large; // large- or huge OS pages (always committed) - _Atomic(uintptr_t) search_idx; // optimization to start the search for free blocks + _Atomic(size_t) search_idx; // optimization to start the search for free blocks mi_bitmap_field_t* blocks_dirty; // are the blocks potentially non-zero? - mi_bitmap_field_t* blocks_committed; // if `!is_committed`, are the blocks committed? + mi_bitmap_field_t* blocks_committed; // are the blocks committed? (can be NULL for memory that cannot be decommitted) mi_bitmap_field_t blocks_inuse[1]; // in-place bitmap of in-use blocks (of size `field_count`) } mi_arena_t; // The available arenas static mi_decl_cache_align _Atomic(mi_arena_t*) mi_arenas[MI_MAX_ARENAS]; -static mi_decl_cache_align _Atomic(uintptr_t) mi_arena_count; // = 0 +static mi_decl_cache_align _Atomic(size_t) mi_arena_count; // = 0 + + +/* ----------------------------------------------------------- + Arena id's + 0 is used for non-arena's (like OS memory) + id = arena_index + 1 +----------------------------------------------------------- */ + +static size_t mi_arena_id_index(mi_arena_id_t id) { + return (size_t)(id <= 0 ? MI_MAX_ARENAS : id - 1); +} + +static mi_arena_id_t mi_arena_id_create(size_t arena_index) { + mi_assert_internal(arena_index < MI_MAX_ARENAS); + mi_assert_internal(MI_MAX_ARENAS <= 126); + int id = (int)arena_index + 1; + mi_assert_internal(id >= 1 && id <= 127); + return id; +} + +mi_arena_id_t _mi_arena_id_none(void) { + return 0; +} + +static bool mi_arena_id_is_suitable(mi_arena_id_t arena_id, bool arena_is_exclusive, mi_arena_id_t req_arena_id) { + return ((!arena_is_exclusive && req_arena_id == _mi_arena_id_none()) || + (arena_id == req_arena_id)); +} /* ----------------------------------------------------------- Arena allocations get a memory id where the lower 8 bits are - the arena index +1, and the upper bits the block index. + the arena id, and the upper bits the block index. ----------------------------------------------------------- */ // Use `0` as a special id for direct OS allocated memory. #define MI_MEMID_OS 0 -static size_t mi_arena_id_create(size_t arena_index, mi_bitmap_index_t bitmap_index) { - mi_assert_internal(arena_index < 0xFE); +static size_t mi_arena_memid_create(mi_arena_id_t id, bool exclusive, mi_bitmap_index_t bitmap_index) { mi_assert_internal(((bitmap_index << 8) >> 8) == bitmap_index); // no overflow? - return ((bitmap_index << 8) | ((arena_index+1) & 0xFF)); + mi_assert_internal(id >= 0 && id <= 0x7F); + return ((bitmap_index << 8) | ((uint8_t)id & 0x7F) | (exclusive ? 0x80 : 0)); } -static void mi_arena_id_indices(size_t memid, size_t* arena_index, mi_bitmap_index_t* bitmap_index) { - mi_assert_internal(memid != MI_MEMID_OS); - *arena_index = (memid & 0xFF) - 1; - *bitmap_index = (memid >> 8); +static bool mi_arena_memid_indices(size_t arena_memid, size_t* arena_index, mi_bitmap_index_t* bitmap_index) { + *bitmap_index = (arena_memid >> 8); + mi_arena_id_t id = (int)(arena_memid & 0x7F); + *arena_index = mi_arena_id_index(id); + return ((arena_memid & 0x80) != 0); +} + +bool _mi_arena_memid_is_suitable(size_t arena_memid, mi_arena_id_t request_arena_id) { + mi_arena_id_t id = (int)(arena_memid & 0x7F); + bool exclusive = ((arena_memid & 0x80) != 0); + return mi_arena_id_is_suitable(id, exclusive, request_arena_id); +} + +bool _mi_arena_is_os_allocated(size_t arena_memid) { + return (arena_memid == MI_MEMID_OS); } static size_t mi_block_count_of_size(size_t size) { @@ -105,9 +129,9 @@ static size_t mi_block_count_of_size(size_t size) { ----------------------------------------------------------- */ static bool mi_arena_alloc(mi_arena_t* arena, size_t blocks, mi_bitmap_index_t* bitmap_idx) { - size_t idx = mi_atomic_load_acquire(&arena->search_idx); // start from last search + size_t idx = 0; // mi_atomic_load_relaxed(&arena->search_idx); // start from last search; ok to be relaxed as the exact start does not matter if (_mi_bitmap_try_find_from_claim_across(arena->blocks_inuse, arena->field_count, idx, blocks, bitmap_idx)) { - mi_atomic_store_release(&arena->search_idx, idx); // start search from here next time + mi_atomic_store_relaxed(&arena->search_idx, mi_bitmap_index_field(*bitmap_idx)); // start search from found location next time around return true; }; return false; @@ -118,19 +142,24 @@ static bool mi_arena_alloc(mi_arena_t* arena, size_t blocks, mi_bitmap_index_t* Arena Allocation ----------------------------------------------------------- */ -static void* mi_arena_alloc_from(mi_arena_t* arena, size_t arena_index, size_t needed_bcount, - bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld) +static mi_decl_noinline void* mi_arena_alloc_from(mi_arena_t* arena, size_t arena_index, size_t needed_bcount, + bool* commit, bool* large, bool* is_pinned, bool* is_zero, + mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld) { + MI_UNUSED(arena_index); + mi_assert_internal(mi_arena_id_index(arena->id) == arena_index); + if (!mi_arena_id_is_suitable(arena->id, arena->exclusive, req_arena_id)) return NULL; + mi_bitmap_index_t bitmap_index; if (!mi_arena_alloc(arena, needed_bcount, &bitmap_index)) return NULL; // claimed it! set the dirty bits (todo: no need for an atomic op here?) void* p = arena->start + (mi_bitmap_index_bit(bitmap_index)*MI_ARENA_BLOCK_SIZE); - *memid = mi_arena_id_create(arena_index, bitmap_index); + *memid = mi_arena_memid_create(arena->id, arena->exclusive, bitmap_index); *is_zero = _mi_bitmap_claim_across(arena->blocks_dirty, arena->field_count, needed_bcount, bitmap_index, NULL); *large = arena->is_large; - *is_pinned = (arena->is_large || arena->is_committed); - if (arena->is_committed) { + *is_pinned = (arena->is_large || !arena->allow_decommit); + if (arena->blocks_committed == NULL) { // always committed *commit = true; } @@ -151,85 +180,126 @@ static void* mi_arena_alloc_from(mi_arena_t* arena, size_t arena_index, size_t n return p; } -void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_pinned, bool* is_zero, - size_t* memid, mi_os_tld_t* tld) +// allocate from an arena with fallback to the OS +static mi_decl_noinline void* mi_arena_allocate(int numa_node, size_t size, size_t alignment, bool* commit, bool* large, + bool* is_pinned, bool* is_zero, + mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld ) { - mi_assert_internal(commit != NULL && is_pinned != NULL && is_zero != NULL && memid != NULL && tld != NULL); - mi_assert_internal(size > 0); - *memid = MI_MEMID_OS; - *is_zero = false; - *is_pinned = false; - - // try to allocate in an arena if the alignment is small enough - // and the object is not too large or too small. - if (alignment <= MI_SEGMENT_ALIGN && - size >= MI_ARENA_MIN_OBJ_SIZE && - mi_atomic_load_relaxed(&mi_arena_count) > 0) - { - const size_t bcount = mi_block_count_of_size(size); - const int numa_node = _mi_os_numa_node(tld); // current numa node - - mi_assert_internal(size <= bcount*MI_ARENA_BLOCK_SIZE); + MI_UNUSED(alignment); + mi_assert_internal(alignment <= MI_SEGMENT_ALIGN); + const size_t max_arena = mi_atomic_load_relaxed(&mi_arena_count); + const size_t bcount = mi_block_count_of_size(size); + if mi_likely(max_arena == 0) return NULL; + mi_assert_internal(size <= bcount * MI_ARENA_BLOCK_SIZE); + + size_t arena_index = mi_arena_id_index(req_arena_id); + if (arena_index < MI_MAX_ARENAS) { + // try a specific arena if requested + mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[arena_index]); + if ((arena != NULL) && + (arena->numa_node < 0 || arena->numa_node == numa_node) && // numa local? + (*large || !arena->is_large)) // large OS pages allowed, or arena is not large OS pages + { + void* p = mi_arena_alloc_from(arena, arena_index, bcount, commit, large, is_pinned, is_zero, req_arena_id, memid, tld); + mi_assert_internal((uintptr_t)p % alignment == 0); + if (p != NULL) return p; + } + } + else { // try numa affine allocation - for (size_t i = 0; i < MI_MAX_ARENAS; i++) { + for (size_t i = 0; i < max_arena; i++) { mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[i]); - if (arena==NULL) break; // end reached - if ((arena->numa_node<0 || arena->numa_node==numa_node) && // numa local? + if (arena == NULL) break; // end reached + if ((arena->numa_node < 0 || arena->numa_node == numa_node) && // numa local? (*large || !arena->is_large)) // large OS pages allowed, or arena is not large OS pages { - void* p = mi_arena_alloc_from(arena, i, bcount, commit, large, is_pinned, is_zero, memid, tld); + void* p = mi_arena_alloc_from(arena, i, bcount, commit, large, is_pinned, is_zero, req_arena_id, memid, tld); mi_assert_internal((uintptr_t)p % alignment == 0); if (p != NULL) return p; } } + // try from another numa node instead.. - for (size_t i = 0; i < MI_MAX_ARENAS; i++) { + for (size_t i = 0; i < max_arena; i++) { mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[i]); - if (arena==NULL) break; // end reached - if ((arena->numa_node>=0 && arena->numa_node!=numa_node) && // not numa local! + if (arena == NULL) break; // end reached + if ((arena->numa_node >= 0 && arena->numa_node != numa_node) && // not numa local! (*large || !arena->is_large)) // large OS pages allowed, or arena is not large OS pages { - void* p = mi_arena_alloc_from(arena, i, bcount, commit, large, is_pinned, is_zero, memid, tld); + void* p = mi_arena_alloc_from(arena, i, bcount, commit, large, is_pinned, is_zero, req_arena_id, memid, tld); mi_assert_internal((uintptr_t)p % alignment == 0); if (p != NULL) return p; } } } + return NULL; +} + +void* _mi_arena_alloc_aligned(size_t size, size_t alignment, size_t align_offset, bool* commit, bool* large, bool* is_pinned, bool* is_zero, + mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld) +{ + mi_assert_internal(commit != NULL && is_pinned != NULL && is_zero != NULL && memid != NULL && tld != NULL); + mi_assert_internal(size > 0); + *memid = MI_MEMID_OS; + *is_zero = false; + *is_pinned = false; + + bool default_large = false; + if (large == NULL) large = &default_large; // ensure `large != NULL` + const int numa_node = _mi_os_numa_node(tld); // current numa node + + // try to allocate in an arena if the alignment is small enough and the object is not too small (as for heap meta data) + if (size >= MI_ARENA_MIN_OBJ_SIZE && alignment <= MI_SEGMENT_ALIGN && align_offset == 0) { + void* p = mi_arena_allocate(numa_node, size, alignment, commit, large, is_pinned, is_zero, req_arena_id, memid, tld); + if (p != NULL) return p; + } // finally, fall back to the OS - if (mi_option_is_enabled(mi_option_limit_os_alloc)) { + if (mi_option_is_enabled(mi_option_limit_os_alloc) || req_arena_id != _mi_arena_id_none()) { errno = ENOMEM; return NULL; } *is_zero = true; - *memid = MI_MEMID_OS; - void* p = _mi_os_alloc_aligned(size, alignment, *commit, large, tld->stats); - if (p != NULL) *is_pinned = *large; + *memid = MI_MEMID_OS; + void* p = _mi_os_alloc_aligned_offset(size, alignment, align_offset, *commit, large, tld->stats); + if (p != NULL) { *is_pinned = *large; } return p; } -void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld) +void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld) { - return _mi_arena_alloc_aligned(size, MI_ARENA_BLOCK_SIZE, commit, large, is_pinned, is_zero, memid, tld); + return _mi_arena_alloc_aligned(size, MI_ARENA_BLOCK_SIZE, 0, commit, large, is_pinned, is_zero, req_arena_id, memid, tld); +} + +void* mi_arena_area(mi_arena_id_t arena_id, size_t* size) { + if (size != NULL) *size = 0; + size_t arena_index = mi_arena_id_index(arena_id); + if (arena_index >= MI_MAX_ARENAS) return NULL; + mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[arena_index]); + if (arena == NULL) return NULL; + if (size != NULL) *size = arena->block_count * MI_ARENA_BLOCK_SIZE; + return arena->start; } /* ----------------------------------------------------------- Arena free ----------------------------------------------------------- */ -void _mi_arena_free(void* p, size_t size, size_t memid, bool all_committed, mi_stats_t* stats) { +void _mi_arena_free(void* p, size_t size, size_t alignment, size_t align_offset, size_t memid, bool all_committed, mi_stats_t* stats) { mi_assert_internal(size > 0 && stats != NULL); if (p==NULL) return; if (size==0) return; + if (memid == MI_MEMID_OS) { // was a direct OS allocation, pass through - _mi_os_free_ex(p, size, all_committed, stats); + _mi_os_free_aligned(p, size, alignment, align_offset, all_committed, stats); } else { // allocated in an arena + mi_assert_internal(align_offset == 0); size_t arena_idx; size_t bitmap_idx; - mi_arena_id_indices(memid, &arena_idx, &bitmap_idx); + mi_arena_memid_indices(memid, &arena_idx, &bitmap_idx); mi_assert_internal(arena_idx < MI_MAX_ARENAS); mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t,&mi_arenas[arena_idx]); mi_assert_internal(arena != NULL); @@ -245,15 +315,15 @@ void _mi_arena_free(void* p, size_t size, size_t memid, bool all_committed, mi_s return; } // potentially decommit - if (arena->is_committed) { - mi_assert_internal(all_committed); + if (!arena->allow_decommit || arena->blocks_committed == NULL) { + mi_assert_internal(all_committed); // note: may be not true as we may "pretend" to be not committed (in segment.c) } else { mi_assert_internal(arena->blocks_committed != NULL); _mi_os_decommit(p, blocks * MI_ARENA_BLOCK_SIZE, stats); // ok if this fails _mi_bitmap_unclaim_across(arena->blocks_committed, arena->field_count, blocks, bitmap_idx); } - // and make it available to others again + // and make it available to others again bool all_inuse = _mi_bitmap_unclaim_across(arena->blocks_inuse, arena->field_count, blocks, bitmap_idx); if (!all_inuse) { _mi_error_message(EAGAIN, "trying to free an already freed block: %p, size %zu\n", p, size); @@ -266,46 +336,58 @@ void _mi_arena_free(void* p, size_t size, size_t memid, bool all_committed, mi_s Add an arena. ----------------------------------------------------------- */ -static bool mi_arena_add(mi_arena_t* arena) { +static bool mi_arena_add(mi_arena_t* arena, mi_arena_id_t* arena_id) { mi_assert_internal(arena != NULL); mi_assert_internal((uintptr_t)mi_atomic_load_ptr_relaxed(uint8_t,&arena->start) % MI_SEGMENT_ALIGN == 0); mi_assert_internal(arena->block_count > 0); + if (arena_id != NULL) *arena_id = -1; - uintptr_t i = mi_atomic_increment_acq_rel(&mi_arena_count); + size_t i = mi_atomic_increment_acq_rel(&mi_arena_count); if (i >= MI_MAX_ARENAS) { mi_atomic_decrement_acq_rel(&mi_arena_count); return false; } mi_atomic_store_ptr_release(mi_arena_t,&mi_arenas[i], arena); + arena->id = mi_arena_id_create(i); + if (arena_id != NULL) *arena_id = arena->id; return true; } -bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) mi_attr_noexcept +bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept { + if (arena_id != NULL) *arena_id = _mi_arena_id_none(); + if (size < MI_ARENA_BLOCK_SIZE) return false; + if (is_large) { mi_assert_internal(is_committed); is_committed = true; } - - const size_t bcount = mi_block_count_of_size(size); + + const size_t bcount = size / MI_ARENA_BLOCK_SIZE; const size_t fields = _mi_divide_up(bcount, MI_BITMAP_FIELD_BITS); const size_t bitmaps = (is_committed ? 2 : 3); const size_t asize = sizeof(mi_arena_t) + (bitmaps*fields*sizeof(mi_bitmap_field_t)); mi_arena_t* arena = (mi_arena_t*)_mi_os_alloc(asize, &_mi_stats_main); // TODO: can we avoid allocating from the OS? if (arena == NULL) return false; + arena->id = _mi_arena_id_none(); + arena->exclusive = exclusive; arena->block_count = bcount; arena->field_count = fields; arena->start = (uint8_t*)start; arena->numa_node = numa_node; // TODO: or get the current numa node if -1? (now it allows anyone to allocate on -1) arena->is_large = is_large; arena->is_zero_init = is_zero; - arena->is_committed = is_committed; + arena->allow_decommit = !is_large && !is_committed; // only allow decommit for initially uncommitted memory arena->search_idx = 0; arena->blocks_dirty = &arena->blocks_inuse[fields]; // just after inuse bitmap - arena->blocks_committed = (is_committed ? NULL : &arena->blocks_inuse[2*fields]); // just after dirty bitmap + arena->blocks_committed = (!arena->allow_decommit ? NULL : &arena->blocks_inuse[2*fields]); // just after dirty bitmap // the bitmaps are already zero initialized due to os_alloc - // just claim leftover blocks if needed + // initialize committed bitmap? + if (arena->blocks_committed != NULL && is_committed) { + memset((void*)arena->blocks_committed, 0xFF, fields*sizeof(mi_bitmap_field_t)); // cast to void* to avoid atomic warning + } + // and claim leftover blocks if needed (so we never allocate there) ptrdiff_t post = (fields * MI_BITMAP_FIELD_BITS) - bcount; mi_assert_internal(post >= 0); if (post > 0) { @@ -314,32 +396,75 @@ bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_la _mi_bitmap_claim(arena->blocks_inuse, fields, post, postidx, NULL); } - mi_arena_add(arena); - return true; + return mi_arena_add(arena, arena_id); + } // Reserve a range of regular OS memory -int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noexcept +int mi_reserve_os_memory_ex(size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept { - size = _mi_os_good_alloc_size(size); + if (arena_id != NULL) *arena_id = _mi_arena_id_none(); + size = _mi_align_up(size, MI_ARENA_BLOCK_SIZE); // at least one block bool large = allow_large; void* start = _mi_os_alloc_aligned(size, MI_SEGMENT_ALIGN, commit, &large, &_mi_stats_main); if (start==NULL) return ENOMEM; - if (!mi_manage_os_memory(start, size, (large || commit), large, true, -1)) { + if (!mi_manage_os_memory_ex(start, size, (large || commit), large, true, -1, exclusive, arena_id)) { _mi_os_free_ex(start, size, commit, &_mi_stats_main); _mi_verbose_message("failed to reserve %zu k memory\n", _mi_divide_up(size,1024)); return ENOMEM; } - _mi_verbose_message("reserved %zu kb memory%s\n", _mi_divide_up(size,1024), large ? " (in large os pages)" : ""); + _mi_verbose_message("reserved %zu KiB memory%s\n", _mi_divide_up(size,1024), large ? " (in large os pages)" : ""); return 0; } +bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) mi_attr_noexcept { + return mi_manage_os_memory_ex(start, size, is_committed, is_large, is_zero, numa_node, false, NULL); +} + +int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noexcept { + return mi_reserve_os_memory_ex(size, commit, allow_large, false, NULL); +} + + +/* ----------------------------------------------------------- + Debugging +----------------------------------------------------------- */ + +static size_t mi_debug_show_bitmap(const char* prefix, mi_bitmap_field_t* fields, size_t field_count ) { + size_t inuse_count = 0; + for (size_t i = 0; i < field_count; i++) { + char buf[MI_BITMAP_FIELD_BITS + 1]; + uintptr_t field = mi_atomic_load_relaxed(&fields[i]); + for (size_t bit = 0; bit < MI_BITMAP_FIELD_BITS; bit++) { + bool inuse = ((((uintptr_t)1 << bit) & field) != 0); + if (inuse) inuse_count++; + buf[MI_BITMAP_FIELD_BITS - 1 - bit] = (inuse ? 'x' : '.'); + } + buf[MI_BITMAP_FIELD_BITS] = 0; + _mi_verbose_message("%s%s\n", prefix, buf); + } + return inuse_count; +} + +void mi_debug_show_arenas(void) mi_attr_noexcept { + size_t max_arenas = mi_atomic_load_relaxed(&mi_arena_count); + for (size_t i = 0; i < max_arenas; i++) { + mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[i]); + if (arena == NULL) break; + size_t inuse_count = 0; + _mi_verbose_message("arena %zu: %zu blocks with %zu fields\n", i, arena->block_count, arena->field_count); + inuse_count += mi_debug_show_bitmap(" ", arena->blocks_inuse, arena->field_count); + _mi_verbose_message(" blocks in use ('x'): %zu\n", inuse_count); + } +} + /* ----------------------------------------------------------- Reserve a huge page arena. ----------------------------------------------------------- */ // reserve at a specific numa node -int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs) mi_attr_noexcept { +int mi_reserve_huge_os_pages_at_ex(size_t pages, int numa_node, size_t timeout_msecs, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept { + if (arena_id != NULL) *arena_id = -1; if (pages==0) return 0; if (numa_node < -1) numa_node = -1; if (numa_node >= 0) numa_node = numa_node % _mi_os_numa_node_count(); @@ -347,18 +472,21 @@ int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msec size_t pages_reserved = 0; void* p = _mi_os_alloc_huge_os_pages(pages, numa_node, timeout_msecs, &pages_reserved, &hsize); if (p==NULL || pages_reserved==0) { - _mi_warning_message("failed to reserve %zu gb huge pages\n", pages); + _mi_warning_message("failed to reserve %zu GiB huge pages\n", pages); return ENOMEM; } - _mi_verbose_message("numa node %i: reserved %zu gb huge pages (of the %zu gb requested)\n", numa_node, pages_reserved, pages); + _mi_verbose_message("numa node %i: reserved %zu GiB huge pages (of the %zu GiB requested)\n", numa_node, pages_reserved, pages); - if (!mi_manage_os_memory(p, hsize, true, true, true, numa_node)) { + if (!mi_manage_os_memory_ex(p, hsize, true, true, true, numa_node, exclusive, arena_id)) { _mi_os_free_huge_pages(p, hsize, &_mi_stats_main); return ENOMEM; } return 0; } +int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs) mi_attr_noexcept { + return mi_reserve_huge_os_pages_at_ex(pages, numa_node, timeout_msecs, false, NULL); +} // reserve huge pages evenly among the given number of numa nodes (or use the available ones as detected) int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs) mi_attr_noexcept { @@ -389,7 +517,7 @@ int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t } int mi_reserve_huge_os_pages(size_t pages, double max_secs, size_t* pages_reserved) mi_attr_noexcept { - UNUSED(max_secs); + MI_UNUSED(max_secs); _mi_warning_message("mi_reserve_huge_os_pages is deprecated: use mi_reserve_huge_os_pages_interleave/at instead\n"); if (pages_reserved != NULL) *pages_reserved = 0; int err = mi_reserve_huge_os_pages_interleave(pages, 0, (size_t)(max_secs * 1000.0)); diff --git a/Source/mimalloc/src/bitmap.c b/Source/mimalloc/src/bitmap.c index 3b5c8199c..ee94edb98 100644 --- a/Source/mimalloc/src/bitmap.c +++ b/Source/mimalloc/src/bitmap.c @@ -7,7 +7,7 @@ terms of the MIT license. A copy of the license can be found in the file /* ---------------------------------------------------------------------------- Concurrent bitmap that can set/reset sequences of bits atomically, -represeted as an array of fields where each field is a machine word (`uintptr_t`) +represeted as an array of fields where each field is a machine word (`size_t`) There are two api's; the standard one cannot have sequences that cross between the bitmap fields (and a sequence must be <= MI_BITMAP_FIELD_BITS). @@ -18,7 +18,7 @@ between the fields. (This is used in arena allocation) ---------------------------------------------------------------------------- */ #include "mimalloc.h" -#include "mimalloc-internal.h" +#include "mimalloc/internal.h" #include "bitmap.h" /* ----------------------------------------------------------- @@ -26,47 +26,47 @@ between the fields. (This is used in arena allocation) ----------------------------------------------------------- */ // The bit mask for a given number of blocks at a specified bit index. -static inline uintptr_t mi_bitmap_mask_(size_t count, size_t bitidx) { +static inline size_t mi_bitmap_mask_(size_t count, size_t bitidx) { mi_assert_internal(count + bitidx <= MI_BITMAP_FIELD_BITS); mi_assert_internal(count > 0); if (count >= MI_BITMAP_FIELD_BITS) return MI_BITMAP_FIELD_FULL; if (count == 0) return 0; - return ((((uintptr_t)1 << count) - 1) << bitidx); + return ((((size_t)1 << count) - 1) << bitidx); } - /* ----------------------------------------------------------- Claim a bit sequence atomically ----------------------------------------------------------- */ // Try to atomically claim a sequence of `count` bits in a single // field at `idx` in `bitmap`. Returns `true` on success. -bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_t count, mi_bitmap_index_t* bitmap_idx) +inline bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_t count, mi_bitmap_index_t* bitmap_idx) { mi_assert_internal(bitmap_idx != NULL); mi_assert_internal(count <= MI_BITMAP_FIELD_BITS); - _Atomic(uintptr_t)* field = &bitmap[idx]; - uintptr_t map = mi_atomic_load_relaxed(field); + mi_assert_internal(count > 0); + mi_bitmap_field_t* field = &bitmap[idx]; + size_t map = mi_atomic_load_relaxed(field); if (map==MI_BITMAP_FIELD_FULL) return false; // short cut // search for 0-bit sequence of length count - const uintptr_t mask = mi_bitmap_mask_(count, 0); - const size_t bitidx_max = MI_BITMAP_FIELD_BITS - count; + const size_t mask = mi_bitmap_mask_(count, 0); + const size_t bitidx_max = MI_BITMAP_FIELD_BITS - count; #ifdef MI_HAVE_FAST_BITSCAN size_t bitidx = mi_ctz(~map); // quickly find the first zero bit if possible #else size_t bitidx = 0; // otherwise start at 0 #endif - uintptr_t m = (mask << bitidx); // invariant: m == mask shifted by bitidx + size_t m = (mask << bitidx); // invariant: m == mask shifted by bitidx // scan linearly for a free range of zero bits while (bitidx <= bitidx_max) { - const uintptr_t mapm = map & m; + const size_t mapm = map & m; if (mapm == 0) { // are the mask bits free at bitidx? mi_assert_internal((m >> bitidx) == mask); // no overflow? - const uintptr_t newmap = map | m; + const size_t newmap = map | m; mi_assert_internal((newmap^map) >> bitidx == mask); if (!mi_atomic_cas_weak_acq_rel(field, &map, newmap)) { // TODO: use strong cas here? // no success, another thread claimed concurrently.. keep going (with updated `map`) @@ -94,9 +94,9 @@ bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_ return false; } - +// Find `count` bits of 0 and set them to 1 atomically; returns `true` on success. // Starts at idx, and wraps around to search in all `bitmap_fields` fields. -// For now, `count` can be at most MI_BITMAP_FIELD_BITS and will never cross fields. +// `count` can be at most MI_BITMAP_FIELD_BITS and will never cross fields. bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx) { size_t idx = start_field_idx; for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) { @@ -108,6 +108,25 @@ bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fiel return false; } +// Like _mi_bitmap_try_find_from_claim but with an extra predicate that must be fullfilled +bool _mi_bitmap_try_find_from_claim_pred(mi_bitmap_t bitmap, const size_t bitmap_fields, + const size_t start_field_idx, const size_t count, + mi_bitmap_pred_fun_t pred_fun, void* pred_arg, + mi_bitmap_index_t* bitmap_idx) { + size_t idx = start_field_idx; + for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) { + if (idx >= bitmap_fields) idx = 0; // wrap + if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) { + if (pred_fun == NULL || pred_fun(*bitmap_idx, pred_arg)) { + return true; + } + // predicate returned false, unclaim and look further + _mi_bitmap_unclaim(bitmap, bitmap_fields, count, *bitmap_idx); + } + } + return false; +} + /* // Find `count` bits of 0 and set them to 1 atomically; returns `true` on success. // For now, `count` can be at most MI_BITMAP_FIELD_BITS and will never span fields. @@ -118,13 +137,13 @@ bool _mi_bitmap_try_find_claim(mi_bitmap_t bitmap, const size_t bitmap_fields, c // Set `count` bits at `bitmap_idx` to 0 atomically // Returns `true` if all `count` bits were 1 previously. -bool mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) { +bool _mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) { const size_t idx = mi_bitmap_index_field(bitmap_idx); const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx); - const uintptr_t mask = mi_bitmap_mask_(count, bitidx); - mi_assert_internal(bitmap_fields > idx); UNUSED(bitmap_fields); + const size_t mask = mi_bitmap_mask_(count, bitidx); + mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields); // mi_assert_internal((bitmap[idx] & mask) == mask); - uintptr_t prev = mi_atomic_and_acq_rel(&bitmap[idx], ~mask); + size_t prev = mi_atomic_and_acq_rel(&bitmap[idx], ~mask); return ((prev & mask) == mask); } @@ -134,10 +153,10 @@ bool mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, m bool _mi_bitmap_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_zero) { const size_t idx = mi_bitmap_index_field(bitmap_idx); const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx); - const uintptr_t mask = mi_bitmap_mask_(count, bitidx); - mi_assert_internal(bitmap_fields > idx); UNUSED(bitmap_fields); + const size_t mask = mi_bitmap_mask_(count, bitidx); + mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields); //mi_assert_internal(any_zero != NULL || (bitmap[idx] & mask) == 0); - uintptr_t prev = mi_atomic_or_acq_rel(&bitmap[idx], mask); + size_t prev = mi_atomic_or_acq_rel(&bitmap[idx], mask); if (any_zero != NULL) *any_zero = ((prev & mask) != mask); return ((prev & mask) == 0); } @@ -146,9 +165,9 @@ bool _mi_bitmap_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi static bool mi_bitmap_is_claimedx(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_ones) { const size_t idx = mi_bitmap_index_field(bitmap_idx); const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx); - const uintptr_t mask = mi_bitmap_mask_(count, bitidx); - mi_assert_internal(bitmap_fields > idx); UNUSED(bitmap_fields); - uintptr_t field = mi_atomic_load_relaxed(&bitmap[idx]); + const size_t mask = mi_bitmap_mask_(count, bitidx); + mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields); + size_t field = mi_atomic_load_relaxed(&bitmap[idx]); if (any_ones != NULL) *any_ones = ((field & mask) != 0); return ((field & mask) == mask); } @@ -169,15 +188,15 @@ bool _mi_bitmap_is_any_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t // between the fields. This is used in arena allocation //-------------------------------------------------------------------------- -// Try to atomically claim a sequence of `count` bits starting from the field +// Try to atomically claim a sequence of `count` bits starting from the field // at `idx` in `bitmap` and crossing into subsequent fields. Returns `true` on success. static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t idx, const size_t count, const size_t retries, mi_bitmap_index_t* bitmap_idx) { mi_assert_internal(bitmap_idx != NULL); - + // check initial trailing zeros - _Atomic(uintptr_t)* field = &bitmap[idx]; - uintptr_t map = mi_atomic_load_relaxed(field); + mi_bitmap_field_t* field = &bitmap[idx]; + size_t map = mi_atomic_load_relaxed(field); const size_t initial = mi_clz(map); // count of initial zeros starting at idx mi_assert_internal(initial <= MI_BITMAP_FIELD_BITS); if (initial == 0) return false; @@ -186,11 +205,11 @@ static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bit // scan ahead size_t found = initial; - uintptr_t mask = 0; // mask bits for the final field + size_t mask = 0; // mask bits for the final field while(found < count) { field++; map = mi_atomic_load_relaxed(field); - const uintptr_t mask_bits = (found + MI_BITMAP_FIELD_BITS <= count ? MI_BITMAP_FIELD_BITS : (count - found)); + const size_t mask_bits = (found + MI_BITMAP_FIELD_BITS <= count ? MI_BITMAP_FIELD_BITS : (count - found)); mask = mi_bitmap_mask_(mask_bits, 0); if ((map & mask) != 0) return false; found += mask_bits; @@ -199,27 +218,27 @@ static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bit // found range of zeros up to the final field; mask contains mask in the final field // now claim it atomically - _Atomic(uintptr_t)* const final_field = field; - const uintptr_t final_mask = mask; - _Atomic(uintptr_t)* const initial_field = &bitmap[idx]; - const uintptr_t initial_mask = mi_bitmap_mask_(initial, MI_BITMAP_FIELD_BITS - initial); + mi_bitmap_field_t* const final_field = field; + const size_t final_mask = mask; + mi_bitmap_field_t* const initial_field = &bitmap[idx]; + const size_t initial_mask = mi_bitmap_mask_(initial, MI_BITMAP_FIELD_BITS - initial); // initial field - uintptr_t newmap; + size_t newmap; field = initial_field; map = mi_atomic_load_relaxed(field); do { newmap = map | initial_mask; if ((map & initial_mask) != 0) { goto rollback; }; } while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)); - + // intermediate fields while (++field < final_field) { - newmap = mi_bitmap_mask_(MI_BITMAP_FIELD_BITS, 0); + newmap = MI_BITMAP_FIELD_FULL; map = 0; if (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)) { goto rollback; } } - + // final field mi_assert_internal(field == final_field); map = mi_atomic_load_relaxed(field); @@ -232,11 +251,11 @@ static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bit *bitmap_idx = mi_bitmap_index_create(idx, MI_BITMAP_FIELD_BITS - initial); return true; -rollback: +rollback: // roll back intermediate fields while (--field > initial_field) { newmap = 0; - map = mi_bitmap_mask_(MI_BITMAP_FIELD_BITS, 0); + map = MI_BITMAP_FIELD_FULL; mi_assert_internal(mi_atomic_load_relaxed(field) == map); mi_atomic_store_release(field, newmap); } @@ -246,7 +265,7 @@ static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bit mi_assert_internal((map & initial_mask) == initial_mask); newmap = map & ~initial_mask; } while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)); - } + } // retry? (we make a recursive call instead of goto to be able to use const declarations) if (retries < 4) { return mi_bitmap_try_find_claim_field_across(bitmap, bitmap_fields, idx, count, retries+1, bitmap_idx); @@ -280,10 +299,10 @@ bool _mi_bitmap_try_find_from_claim_across(mi_bitmap_t bitmap, const size_t bitm } // Helper for masks across fields; returns the mid count, post_mask may be 0 -static size_t mi_bitmap_mask_across(mi_bitmap_index_t bitmap_idx, size_t bitmap_fields, size_t count, uintptr_t* pre_mask, uintptr_t* mid_mask, uintptr_t* post_mask) { - UNUSED_RELEASE(bitmap_fields); +static size_t mi_bitmap_mask_across(mi_bitmap_index_t bitmap_idx, size_t bitmap_fields, size_t count, size_t* pre_mask, size_t* mid_mask, size_t* post_mask) { + MI_UNUSED(bitmap_fields); const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx); - if (mi_likely(bitidx + count <= MI_BITMAP_FIELD_BITS)) { + if mi_likely(bitidx + count <= MI_BITMAP_FIELD_BITS) { *pre_mask = mi_bitmap_mask_(count, bitidx); *mid_mask = 0; *post_mask = 0; @@ -308,13 +327,13 @@ static size_t mi_bitmap_mask_across(mi_bitmap_index_t bitmap_idx, size_t bitmap_ // Returns `true` if all `count` bits were 1 previously. bool _mi_bitmap_unclaim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) { size_t idx = mi_bitmap_index_field(bitmap_idx); - uintptr_t pre_mask; - uintptr_t mid_mask; - uintptr_t post_mask; - size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask); + size_t pre_mask; + size_t mid_mask; + size_t post_mask; + size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask); bool all_one = true; - _Atomic(uintptr_t)*field = &bitmap[idx]; - uintptr_t prev = mi_atomic_and_acq_rel(field++, ~pre_mask); + mi_bitmap_field_t* field = &bitmap[idx]; + size_t prev = mi_atomic_and_acq_rel(field++, ~pre_mask); if ((prev & pre_mask) != pre_mask) all_one = false; while(mid_count-- > 0) { prev = mi_atomic_and_acq_rel(field++, ~mid_mask); @@ -324,21 +343,21 @@ bool _mi_bitmap_unclaim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t prev = mi_atomic_and_acq_rel(field, ~post_mask); if ((prev & post_mask) != post_mask) all_one = false; } - return all_one; + return all_one; } // Set `count` bits at `bitmap_idx` to 1 atomically // Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit. bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_zero) { size_t idx = mi_bitmap_index_field(bitmap_idx); - uintptr_t pre_mask; - uintptr_t mid_mask; - uintptr_t post_mask; + size_t pre_mask; + size_t mid_mask; + size_t post_mask; size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask); bool all_zero = true; bool any_zero = false; - _Atomic(uintptr_t)*field = &bitmap[idx]; - uintptr_t prev = mi_atomic_or_acq_rel(field++, pre_mask); + _Atomic(size_t)*field = &bitmap[idx]; + size_t prev = mi_atomic_or_acq_rel(field++, pre_mask); if ((prev & pre_mask) != 0) all_zero = false; if ((prev & pre_mask) != pre_mask) any_zero = true; while (mid_count-- > 0) { @@ -356,18 +375,18 @@ bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t co } -// Returns `true` if all `count` bits were 1. +// Returns `true` if all `count` bits were 1. // `any_ones` is `true` if there was at least one bit set to one. static bool mi_bitmap_is_claimedx_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_ones) { size_t idx = mi_bitmap_index_field(bitmap_idx); - uintptr_t pre_mask; - uintptr_t mid_mask; - uintptr_t post_mask; + size_t pre_mask; + size_t mid_mask; + size_t post_mask; size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask); bool all_ones = true; bool any_ones = false; - _Atomic(uintptr_t)* field = &bitmap[idx]; - uintptr_t prev = mi_atomic_load_relaxed(field++); + mi_bitmap_field_t* field = &bitmap[idx]; + size_t prev = mi_atomic_load_relaxed(field++); if ((prev & pre_mask) != pre_mask) all_ones = false; if ((prev & pre_mask) != 0) any_ones = true; while (mid_count-- > 0) { @@ -379,7 +398,7 @@ static bool mi_bitmap_is_claimedx_across(mi_bitmap_t bitmap, size_t bitmap_field prev = mi_atomic_load_relaxed(field); if ((prev & post_mask) != post_mask) all_ones = false; if ((prev & post_mask) != 0) any_ones = true; - } + } if (pany_ones != NULL) *pany_ones = any_ones; return all_ones; } diff --git a/Source/mimalloc/src/bitmap.h b/Source/mimalloc/src/bitmap.h index 21fd4e13d..3476ea46b 100644 --- a/Source/mimalloc/src/bitmap.h +++ b/Source/mimalloc/src/bitmap.h @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2019-2020 Microsoft Research, Daan Leijen +Copyright (c) 2019-2023 Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -7,7 +7,7 @@ terms of the MIT license. A copy of the license can be found in the file /* ---------------------------------------------------------------------------- Concurrent bitmap that can set/reset sequences of bits atomically, -represeted as an array of fields where each field is a machine word (`uintptr_t`) +represeted as an array of fields where each field is a machine word (`size_t`) There are two api's; the standard one cannot have sequences that cross between the bitmap fields (and a sequence must be <= MI_BITMAP_FIELD_BITS). @@ -24,11 +24,11 @@ between the fields. (This is used in arena allocation) Bitmap definition ----------------------------------------------------------- */ -#define MI_BITMAP_FIELD_BITS (8*MI_INTPTR_SIZE) -#define MI_BITMAP_FIELD_FULL (~((uintptr_t)0)) // all bits set +#define MI_BITMAP_FIELD_BITS (8*MI_SIZE_SIZE) +#define MI_BITMAP_FIELD_FULL (~((size_t)0)) // all bits set -// An atomic bitmap of `uintptr_t` fields -typedef _Atomic(uintptr_t) mi_bitmap_field_t; +// An atomic bitmap of `size_t` fields +typedef _Atomic(size_t) mi_bitmap_field_t; typedef mi_bitmap_field_t* mi_bitmap_t; // A bitmap index is the index of the bit in a bitmap. @@ -40,6 +40,11 @@ static inline mi_bitmap_index_t mi_bitmap_index_create(size_t idx, size_t bitidx return (idx*MI_BITMAP_FIELD_BITS) + bitidx; } +// Create a bit index. +static inline mi_bitmap_index_t mi_bitmap_index_create_from_bit(size_t full_bitidx) { + return mi_bitmap_index_create(full_bitidx / MI_BITMAP_FIELD_BITS, full_bitidx % MI_BITMAP_FIELD_BITS); +} + // Get the field index from a bit index. static inline size_t mi_bitmap_index_field(mi_bitmap_index_t bitmap_idx) { return (bitmap_idx / MI_BITMAP_FIELD_BITS); @@ -67,9 +72,13 @@ bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_ // For now, `count` can be at most MI_BITMAP_FIELD_BITS and will never cross fields. bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx); +// Like _mi_bitmap_try_find_from_claim but with an extra predicate that must be fullfilled +typedef bool (mi_cdecl *mi_bitmap_pred_fun_t)(mi_bitmap_index_t bitmap_idx, void* pred_arg); +bool _mi_bitmap_try_find_from_claim_pred(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_pred_fun_t pred_fun, void* pred_arg, mi_bitmap_index_t* bitmap_idx); + // Set `count` bits at `bitmap_idx` to 0 atomically // Returns `true` if all `count` bits were 1 previously. -bool mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx); +bool _mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx); // Set `count` bits at `bitmap_idx` to 1 atomically // Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit. diff --git a/Source/mimalloc/src/heap.c b/Source/mimalloc/src/heap.c index bda10699d..7103281f0 100644 --- a/Source/mimalloc/src/heap.c +++ b/Source/mimalloc/src/heap.c @@ -6,8 +6,9 @@ terms of the MIT license. A copy of the license can be found in the file -----------------------------------------------------------------------------*/ #include "mimalloc.h" -#include "mimalloc-internal.h" -#include "mimalloc-atomic.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" +#include "mimalloc/prim.h" // mi_prim_get_default_heap #include // memset, memcpy @@ -30,15 +31,18 @@ static bool mi_heap_visit_pages(mi_heap_t* heap, heap_page_visitor_fun* fn, void // visit all pages #if MI_DEBUG>1 size_t total = heap->page_count; - #endif size_t count = 0; + #endif + for (size_t i = 0; i <= MI_BIN_FULL; i++) { mi_page_queue_t* pq = &heap->pages[i]; mi_page_t* page = pq->first; while(page != NULL) { mi_page_t* next = page->next; // save next in case the page gets removed from the queue mi_assert_internal(mi_page_heap(page) == heap); + #if MI_DEBUG>1 count++; + #endif if (!fn(heap, pq, page, arg1, arg2)) return false; page = next; // and continue } @@ -50,9 +54,9 @@ static bool mi_heap_visit_pages(mi_heap_t* heap, heap_page_visitor_fun* fn, void #if MI_DEBUG>=2 static bool mi_heap_page_is_valid(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) { - UNUSED(arg1); - UNUSED(arg2); - UNUSED(pq); + MI_UNUSED(arg1); + MI_UNUSED(arg2); + MI_UNUSED(pq); mi_assert_internal(mi_page_heap(page) == heap); mi_segment_t* segment = _mi_page_segment(page); mi_assert_internal(segment->thread_id == heap->thread_id); @@ -86,13 +90,13 @@ typedef enum mi_collect_e { static bool mi_heap_page_collect(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg_collect, void* arg2 ) { - UNUSED(arg2); - UNUSED(heap); + MI_UNUSED(arg2); + MI_UNUSED(heap); mi_assert_internal(mi_heap_page_is_valid(heap, pq, page, NULL, NULL)); mi_collect_t collect = *((mi_collect_t*)arg_collect); _mi_page_free_collect(page, collect >= MI_FORCE); if (mi_page_all_free(page)) { - // no more used blocks, free the page. + // no more used blocks, free the page. // note: this will free retired pages as well. _mi_page_free(page, pq, collect >= MI_FORCE); } @@ -104,10 +108,10 @@ static bool mi_heap_page_collect(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t } static bool mi_heap_page_never_delayed_free(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) { - UNUSED(arg1); - UNUSED(arg2); - UNUSED(heap); - UNUSED(pq); + MI_UNUSED(arg1); + MI_UNUSED(arg2); + MI_UNUSED(heap); + MI_UNUSED(pq); _mi_page_use_delayed_free(page, MI_NEVER_DELAYED_FREE, false); return true; // don't break } @@ -115,46 +119,57 @@ static bool mi_heap_page_never_delayed_free(mi_heap_t* heap, mi_page_queue_t* pq static void mi_heap_collect_ex(mi_heap_t* heap, mi_collect_t collect) { if (heap==NULL || !mi_heap_is_initialized(heap)) return; - _mi_deferred_free(heap, collect >= MI_FORCE); + + const bool force = collect >= MI_FORCE; + _mi_deferred_free(heap, force); // note: never reclaim on collect but leave it to threads that need storage to reclaim - if ( - #ifdef NDEBUG + const bool force_main = + #ifdef NDEBUG collect == MI_FORCE - #else + #else collect >= MI_FORCE - #endif - && _mi_is_main_thread() && mi_heap_is_backing(heap) && !heap->no_reclaim) - { + #endif + && _mi_is_main_thread() && mi_heap_is_backing(heap) && !heap->no_reclaim; + + if (force_main) { // the main thread is abandoned (end-of-program), try to reclaim all abandoned segments. // if all memory is freed by now, all segments should be freed. _mi_abandoned_reclaim_all(heap, &heap->tld->segments); } - + // if abandoning, mark all pages to no longer add to delayed_free if (collect == MI_ABANDON) { mi_heap_visit_pages(heap, &mi_heap_page_never_delayed_free, NULL, NULL); } - // free thread delayed blocks. + // free all current thread delayed blocks. // (if abandoning, after this there are no more thread-delayed references into the pages.) - _mi_heap_delayed_free(heap); + _mi_heap_delayed_free_all(heap); // collect retired pages - _mi_heap_collect_retired(heap, collect >= MI_FORCE); + _mi_heap_collect_retired(heap, force); // collect all pages owned by this thread mi_heap_visit_pages(heap, &mi_heap_page_collect, &collect, NULL); mi_assert_internal( collect != MI_ABANDON || mi_atomic_load_ptr_acquire(mi_block_t,&heap->thread_delayed_free) == NULL ); - // collect segment caches - if (collect >= MI_FORCE) { + // collect abandoned segments (in particular, decommit expired parts of segments in the abandoned segment list) + // note: forced decommit can be quite expensive if many threads are created/destroyed so we do not force on abandonment + _mi_abandoned_collect(heap, collect == MI_FORCE /* force? */, &heap->tld->segments); + + // collect segment local caches + if (force) { _mi_segment_thread_collect(&heap->tld->segments); } + // decommit in global segment caches + // note: forced decommit can be quite expensive if many threads are created/destroyed so we do not force on abandonment + _mi_segment_cache_collect( collect == MI_FORCE, &heap->tld->os); + // collect regions on program-exit (or shared library unload) - if (collect >= MI_FORCE && _mi_is_main_thread() && mi_heap_is_backing(heap)) { - _mi_mem_collect(&heap->tld->os); + if (force && _mi_is_main_thread() && mi_heap_is_backing(heap)) { + //_mi_mem_collect(&heap->tld->os); } } @@ -167,7 +182,7 @@ void mi_heap_collect(mi_heap_t* heap, bool force) mi_attr_noexcept { } void mi_collect(bool force) mi_attr_noexcept { - mi_heap_collect(mi_get_default_heap(), force); + mi_heap_collect(mi_prim_get_default_heap(), force); } @@ -177,9 +192,14 @@ void mi_collect(bool force) mi_attr_noexcept { mi_heap_t* mi_heap_get_default(void) { mi_thread_init(); - return mi_get_default_heap(); + return mi_prim_get_default_heap(); } +static bool mi_heap_is_default(const mi_heap_t* heap) { + return (heap == mi_prim_get_default_heap()); +} + + mi_heap_t* mi_heap_get_backing(void) { mi_heap_t* heap = mi_heap_get_default(); mi_assert_internal(heap!=NULL); @@ -189,13 +209,14 @@ mi_heap_t* mi_heap_get_backing(void) { return bheap; } -mi_heap_t* mi_heap_new(void) { +mi_decl_nodiscard mi_heap_t* mi_heap_new_in_arena( mi_arena_id_t arena_id ) { mi_heap_t* bheap = mi_heap_get_backing(); mi_heap_t* heap = mi_heap_malloc_tp(bheap, mi_heap_t); // todo: OS allocate in secure mode? if (heap==NULL) return NULL; _mi_memcpy_aligned(heap, &_mi_heap_empty, sizeof(mi_heap_t)); heap->tld = bheap->tld; heap->thread_id = _mi_thread_id(); + heap->arena_id = arena_id; _mi_random_split(&bheap->random, &heap->random); heap->cookie = _mi_heap_random_next(heap) | 1; heap->keys[0] = _mi_heap_random_next(heap); @@ -207,6 +228,14 @@ mi_heap_t* mi_heap_new(void) { return heap; } +mi_decl_nodiscard mi_heap_t* mi_heap_new(void) { + return mi_heap_new_in_arena(_mi_arena_id_none()); +} + +bool _mi_heap_memid_is_suitable(mi_heap_t* heap, size_t memid) { + return _mi_arena_memid_is_suitable(memid, heap->arena_id); +} + uintptr_t _mi_heap_random_next(mi_heap_t* heap) { return _mi_random_next(&heap->random); } @@ -217,9 +246,6 @@ static void mi_heap_reset_pages(mi_heap_t* heap) { mi_assert_internal(mi_heap_is_initialized(heap)); // TODO: copy full empty heap instead? memset(&heap->pages_free_direct, 0, sizeof(heap->pages_free_direct)); -#ifdef MI_MEDIUM_DIRECT - memset(&heap->pages_free_medium, 0, sizeof(heap->pages_free_medium)); -#endif _mi_memcpy_aligned(&heap->pages, &_mi_heap_empty.pages, sizeof(heap->pages)); heap->thread_delayed_free = NULL; heap->page_count = 0; @@ -240,7 +266,7 @@ static void mi_heap_free(mi_heap_t* heap) { // remove ourselves from the thread local heaps list // linear search but we expect the number of heaps to be relatively small mi_heap_t* prev = NULL; - mi_heap_t* curr = heap->tld->heaps; + mi_heap_t* curr = heap->tld->heaps; while (curr != heap && curr != NULL) { prev = curr; curr = curr->next; @@ -262,19 +288,19 @@ static void mi_heap_free(mi_heap_t* heap) { ----------------------------------------------------------- */ static bool _mi_heap_page_destroy(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) { - UNUSED(arg1); - UNUSED(arg2); - UNUSED(heap); - UNUSED(pq); + MI_UNUSED(arg1); + MI_UNUSED(arg2); + MI_UNUSED(heap); + MI_UNUSED(pq); // ensure no more thread_delayed_free will be added _mi_page_use_delayed_free(page, MI_NEVER_DELAYED_FREE, false); // stats const size_t bsize = mi_page_block_size(page); - if (bsize > MI_LARGE_OBJ_SIZE_MAX) { - if (bsize > MI_HUGE_OBJ_SIZE_MAX) { - mi_heap_stat_decrease(heap, giant, bsize); + if (bsize > MI_MEDIUM_OBJ_SIZE_MAX) { + if (bsize <= MI_LARGE_OBJ_SIZE_MAX) { + mi_heap_stat_decrease(heap, large, bsize); } else { mi_heap_stat_decrease(heap, huge, bsize); @@ -310,6 +336,14 @@ void _mi_heap_destroy_pages(mi_heap_t* heap) { mi_heap_reset_pages(heap); } +#if MI_TRACK_HEAP_DESTROY +static bool mi_cdecl mi_heap_track_block_free(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg) { + MI_UNUSED(heap); MI_UNUSED(area); MI_UNUSED(arg); MI_UNUSED(block_size); + mi_track_free_size(block,mi_usable_size(block)); + return true; +} +#endif + void mi_heap_destroy(mi_heap_t* heap) { mi_assert(heap != NULL); mi_assert(mi_heap_is_initialized(heap)); @@ -321,27 +355,44 @@ void mi_heap_destroy(mi_heap_t* heap) { mi_heap_delete(heap); } else { + // track all blocks as freed + #if MI_TRACK_HEAP_DESTROY + mi_heap_visit_blocks(heap, true, mi_heap_track_block_free, NULL); + #endif // free all pages _mi_heap_destroy_pages(heap); mi_heap_free(heap); } } - +void _mi_heap_destroy_all(void) { + mi_heap_t* bheap = mi_heap_get_backing(); + mi_heap_t* curr = bheap->tld->heaps; + while (curr != NULL) { + mi_heap_t* next = curr->next; + if (curr->no_reclaim) { + mi_heap_destroy(curr); + } + else { + _mi_heap_destroy_pages(curr); + } + curr = next; + } +} /* ----------------------------------------------------------- Safe Heap delete ----------------------------------------------------------- */ -// Tranfer the pages from one heap to the other +// Transfer the pages from one heap to the other static void mi_heap_absorb(mi_heap_t* heap, mi_heap_t* from) { mi_assert_internal(heap!=NULL); if (from==NULL || from->page_count == 0) return; // reduce the size of the delayed frees - _mi_heap_delayed_free(from); - - // transfer all pages by appending the queues; this will set a new heap field + _mi_heap_delayed_free_partial(from); + + // transfer all pages by appending the queues; this will set a new heap field // so threads may do delayed frees in either heap for a while. // note: appending waits for each page to not be in the `MI_DELAYED_FREEING` state // so after this only the new heap will get delayed frees @@ -354,17 +405,17 @@ static void mi_heap_absorb(mi_heap_t* heap, mi_heap_t* from) { } mi_assert_internal(from->page_count == 0); - // and do outstanding delayed frees in the `from` heap + // and do outstanding delayed frees in the `from` heap // note: be careful here as the `heap` field in all those pages no longer point to `from`, - // turns out to be ok as `_mi_heap_delayed_free` only visits the list and calls a + // turns out to be ok as `_mi_heap_delayed_free` only visits the list and calls a // the regular `_mi_free_delayed_block` which is safe. - _mi_heap_delayed_free(from); + _mi_heap_delayed_free_all(from); #if !defined(_MSC_VER) || (_MSC_VER > 1900) // somehow the following line gives an error in VS2015, issue #353 mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_block_t,&from->thread_delayed_free) == NULL); #endif // and reset the `from` heap - mi_heap_reset_pages(from); + mi_heap_reset_pages(from); } // Safe delete a heap without freeing any still allocated blocks in that heap. @@ -392,7 +443,7 @@ mi_heap_t* mi_heap_set_default(mi_heap_t* heap) { mi_assert(mi_heap_is_initialized(heap)); if (heap==NULL || !mi_heap_is_initialized(heap)) return NULL; mi_assert_expensive(mi_heap_is_valid(heap)); - mi_heap_t* old = mi_get_default_heap(); + mi_heap_t* old = mi_prim_get_default_heap(); _mi_heap_set_default_direct(heap); return old; } @@ -410,7 +461,7 @@ static mi_heap_t* mi_heap_of_block(const void* p) { mi_segment_t* segment = _mi_ptr_segment(p); bool valid = (_mi_ptr_cookie(segment) == segment->cookie); mi_assert_internal(valid); - if (mi_unlikely(!valid)) return NULL; + if mi_unlikely(!valid) return NULL; return mi_page_heap(_mi_segment_page_of(segment,p)); } @@ -422,8 +473,8 @@ bool mi_heap_contains_block(mi_heap_t* heap, const void* p) { static bool mi_heap_page_check_owned(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* p, void* vfound) { - UNUSED(heap); - UNUSED(pq); + MI_UNUSED(heap); + MI_UNUSED(pq); bool* found = (bool*)vfound; mi_segment_t* segment = _mi_page_segment(page); void* start = _mi_page_start(segment, page, NULL); @@ -442,7 +493,7 @@ bool mi_heap_check_owned(mi_heap_t* heap, const void* p) { } bool mi_check_owned(const void* p) { - return mi_heap_check_owned(mi_get_default_heap(), p); + return mi_heap_check_owned(mi_prim_get_default_heap(), p); } /* ----------------------------------------------------------- @@ -470,13 +521,14 @@ static bool mi_heap_area_visit_blocks(const mi_heap_area_ex_t* xarea, mi_block_v if (page->used == 0) return true; const size_t bsize = mi_page_block_size(page); + const size_t ubsize = mi_page_usable_block_size(page); // without padding size_t psize; uint8_t* pstart = _mi_page_start(_mi_page_segment(page), page, &psize); if (page->capacity == 1) { // optimize page with one block mi_assert_internal(page->used == 1 && page->free == NULL); - return visitor(mi_page_heap(page), area, pstart, bsize, arg); + return visitor(mi_page_heap(page), area, pstart, ubsize, arg); } // create a bitmap of free blocks. @@ -484,9 +536,13 @@ static bool mi_heap_area_visit_blocks(const mi_heap_area_ex_t* xarea, mi_block_v uintptr_t free_map[MI_MAX_BLOCKS / sizeof(uintptr_t)]; memset(free_map, 0, sizeof(free_map)); + #if MI_DEBUG>1 size_t free_count = 0; + #endif for (mi_block_t* block = page->free; block != NULL; block = mi_block_next(page,block)) { + #if MI_DEBUG>1 free_count++; + #endif mi_assert_internal((uint8_t*)block >= pstart && (uint8_t*)block < (pstart + psize)); size_t offset = (uint8_t*)block - pstart; mi_assert_internal(offset % bsize == 0); @@ -499,7 +555,9 @@ static bool mi_heap_area_visit_blocks(const mi_heap_area_ex_t* xarea, mi_block_v mi_assert_internal(page->capacity == (free_count + page->used)); // walk through all blocks skipping the free ones + #if MI_DEBUG>1 size_t used_count = 0; + #endif for (size_t i = 0; i < page->capacity; i++) { size_t bitidx = (i / sizeof(uintptr_t)); size_t bit = i - (bitidx * sizeof(uintptr_t)); @@ -508,9 +566,11 @@ static bool mi_heap_area_visit_blocks(const mi_heap_area_ex_t* xarea, mi_block_v i += (sizeof(uintptr_t) - 1); // skip a run of free blocks } else if ((m & ((uintptr_t)1 << bit)) == 0) { + #if MI_DEBUG>1 used_count++; + #endif uint8_t* block = pstart + (i * bsize); - if (!visitor(mi_page_heap(page), area, block, bsize, arg)) return false; + if (!visitor(mi_page_heap(page), area, block, ubsize, arg)) return false; } } mi_assert_internal(page->used == used_count); @@ -521,17 +581,19 @@ typedef bool (mi_heap_area_visit_fun)(const mi_heap_t* heap, const mi_heap_area_ static bool mi_heap_visit_areas_page(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* vfun, void* arg) { - UNUSED(heap); - UNUSED(pq); + MI_UNUSED(heap); + MI_UNUSED(pq); mi_heap_area_visit_fun* fun = (mi_heap_area_visit_fun*)vfun; mi_heap_area_ex_t xarea; const size_t bsize = mi_page_block_size(page); + const size_t ubsize = mi_page_usable_block_size(page); xarea.page = page; xarea.area.reserved = page->reserved * bsize; xarea.area.committed = page->capacity * bsize; xarea.area.blocks = _mi_page_start(_mi_page_segment(page), page, NULL); - xarea.area.used = page->used; - xarea.area.block_size = bsize; + xarea.area.used = page->used; // number of blocks in use (#553) + xarea.area.block_size = ubsize; + xarea.area.full_block_size = bsize; return fun(heap, &xarea, arg); } diff --git a/Source/mimalloc/src/init.c b/Source/mimalloc/src/init.c index c0f09b5ed..51d42acd9 100644 --- a/Source/mimalloc/src/init.c +++ b/Source/mimalloc/src/init.c @@ -1,15 +1,17 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2022, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ #include "mimalloc.h" -#include "mimalloc-internal.h" +#include "mimalloc/internal.h" +#include "mimalloc/prim.h" #include // memcpy, memset #include // atexit + // Empty page used to initialize the small free pages array const mi_page_t _mi_page_empty = { 0, false, false, false, false, @@ -19,15 +21,18 @@ const mi_page_t _mi_page_empty = { false, // is_zero 0, // retire_expire NULL, // free - #if MI_ENCODE_FREELIST - { 0, 0 }, - #endif 0, // used 0, // xblock_size NULL, // local_free - ATOMIC_VAR_INIT(0), // xthread_free - ATOMIC_VAR_INIT(0), // xheap + #if (MI_PADDING || MI_ENCODE_FREELIST) + { 0, 0 }, + #endif + MI_ATOMIC_VAR_INIT(0), // xthread_free + MI_ATOMIC_VAR_INIT(0), // xheap NULL, NULL + #if MI_INTPTR_SIZE==8 + , { 0 } // padding + #endif }; #define MI_PAGE_EMPTY() ((mi_page_t*)&_mi_page_empty) @@ -54,8 +59,8 @@ const mi_page_t _mi_page_empty = { QNULL( 10240), QNULL( 12288), QNULL( 14336), QNULL( 16384), QNULL( 20480), QNULL( 24576), QNULL( 28672), QNULL( 32768), /* 56 */ \ QNULL( 40960), QNULL( 49152), QNULL( 57344), QNULL( 65536), QNULL( 81920), QNULL( 98304), QNULL(114688), QNULL(131072), /* 64 */ \ QNULL(163840), QNULL(196608), QNULL(229376), QNULL(262144), QNULL(327680), QNULL(393216), QNULL(458752), QNULL(524288), /* 72 */ \ - QNULL(MI_LARGE_OBJ_WSIZE_MAX + 1 /* 655360, Huge queue */), \ - QNULL(MI_LARGE_OBJ_WSIZE_MAX + 2) /* Full queue */ } + QNULL(MI_MEDIUM_OBJ_WSIZE_MAX + 1 /* 655360, Huge queue */), \ + QNULL(MI_MEDIUM_OBJ_WSIZE_MAX + 2) /* Full queue */ } #define MI_STAT_COUNT_NULL() {0,0,0,0} @@ -78,6 +83,18 @@ const mi_page_t _mi_page_empty = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } \ MI_STAT_COUNT_END_NULL() + +// Empty slice span queues for every bin +#define SQNULL(sz) { NULL, NULL, sz } +#define MI_SEGMENT_SPAN_QUEUES_EMPTY \ + { SQNULL(1), \ + SQNULL( 1), SQNULL( 2), SQNULL( 3), SQNULL( 4), SQNULL( 5), SQNULL( 6), SQNULL( 7), SQNULL( 10), /* 8 */ \ + SQNULL( 12), SQNULL( 14), SQNULL( 16), SQNULL( 20), SQNULL( 24), SQNULL( 28), SQNULL( 32), SQNULL( 40), /* 16 */ \ + SQNULL( 48), SQNULL( 56), SQNULL( 64), SQNULL( 80), SQNULL( 96), SQNULL( 112), SQNULL( 128), SQNULL( 160), /* 24 */ \ + SQNULL( 192), SQNULL( 224), SQNULL( 256), SQNULL( 320), SQNULL( 384), SQNULL( 448), SQNULL( 512), SQNULL( 640), /* 32 */ \ + SQNULL( 768), SQNULL( 896), SQNULL( 1024) /* 35 */ } + + // -------------------------------------------------------- // Statically allocate an empty heap as the initial // thread local value for the default heap, @@ -91,17 +108,34 @@ mi_decl_cache_align const mi_heap_t _mi_heap_empty = { NULL, MI_SMALL_PAGES_EMPTY, MI_PAGE_QUEUES_EMPTY, - ATOMIC_VAR_INIT(NULL), + MI_ATOMIC_VAR_INIT(NULL), 0, // tid 0, // cookie + 0, // arena id { 0, 0 }, // keys - { {0}, {0}, 0 }, + { {0}, {0}, 0, true }, // random 0, // page count MI_BIN_FULL, 0, // page retired min/max NULL, // next false }; +#define tld_empty_stats ((mi_stats_t*)((uint8_t*)&tld_empty + offsetof(mi_tld_t,stats))) +#define tld_empty_os ((mi_os_tld_t*)((uint8_t*)&tld_empty + offsetof(mi_tld_t,os))) + +mi_decl_cache_align static const mi_tld_t tld_empty = { + 0, + false, + NULL, NULL, + { MI_SEGMENT_SPAN_QUEUES_EMPTY, 0, 0, 0, 0, tld_empty_stats, tld_empty_os }, // segments + { 0, tld_empty_stats }, // os + { MI_STATS_NULL } // stats +}; + +mi_threadid_t _mi_thread_id(void) mi_attr_noexcept { + return _mi_prim_thread_id(); +} + // the thread-local default heap for allocation mi_decl_thread mi_heap_t* _mi_heap_default = (mi_heap_t*)&_mi_heap_empty; @@ -109,11 +143,8 @@ extern mi_heap_t _mi_heap_main; static mi_tld_t tld_main = { 0, false, - &_mi_heap_main, &_mi_heap_main, - { { NULL, NULL }, {NULL ,NULL}, {NULL ,NULL, 0}, - 0, 0, 0, 0, 0, 0, NULL, - &tld_main.stats, &tld_main.os - }, // segments + &_mi_heap_main, & _mi_heap_main, + { MI_SEGMENT_SPAN_QUEUES_EMPTY, 0, 0, 0, 0, &tld_main.stats, &tld_main.os }, // segments { 0, &tld_main.stats }, // os { MI_STATS_NULL } // stats }; @@ -122,11 +153,12 @@ mi_heap_t _mi_heap_main = { &tld_main, MI_SMALL_PAGES_EMPTY, MI_PAGE_QUEUES_EMPTY, - ATOMIC_VAR_INIT(NULL), + MI_ATOMIC_VAR_INIT(NULL), 0, // thread id 0, // initial cookie + 0, // arena id { 0, 0 }, // the key of the main heap can be fixed (unlike page keys that need to be secure!) - { {0x846ca68b}, {0}, 0 }, // random + { {0x846ca68b}, {0}, 0, true }, // random 0, // page count MI_BIN_FULL, 0, // page retired min/max NULL, // next heap @@ -141,8 +173,13 @@ mi_stats_t _mi_stats_main = { MI_STATS_NULL }; static void mi_heap_main_init(void) { if (_mi_heap_main.cookie == 0) { _mi_heap_main.thread_id = _mi_thread_id(); - _mi_heap_main.cookie = _os_random_weak((uintptr_t)&mi_heap_main_init); - _mi_random_init(&_mi_heap_main.random); + _mi_heap_main.cookie = 1; + #if defined(_WIN32) && !defined(MI_SHARED_LIB) + _mi_random_init_weak(&_mi_heap_main.random); // prevent allocation failure during bcrypt dll initialization with static linking + #else + _mi_random_init(&_mi_heap_main.random); + #endif + _mi_heap_main.cookie = _mi_heap_random_next(&_mi_heap_main); _mi_heap_main.keys[0] = _mi_heap_random_next(&_mi_heap_main); _mi_heap_main.keys[1] = _mi_heap_random_next(&_mi_heap_main); } @@ -164,31 +201,87 @@ typedef struct mi_thread_data_s { mi_tld_t tld; } mi_thread_data_t; + +// Thread meta-data is allocated directly from the OS. For +// some programs that do not use thread pools and allocate and +// destroy many OS threads, this may causes too much overhead +// per thread so we maintain a small cache of recently freed metadata. + +#define TD_CACHE_SIZE (8) +static _Atomic(mi_thread_data_t*) td_cache[TD_CACHE_SIZE]; + +static mi_thread_data_t* mi_thread_data_alloc(void) { + // try to find thread metadata in the cache + mi_thread_data_t* td; + for (int i = 0; i < TD_CACHE_SIZE; i++) { + td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]); + if (td != NULL) { + td = mi_atomic_exchange_ptr_acq_rel(mi_thread_data_t, &td_cache[i], NULL); + if (td != NULL) { + return td; + } + } + } + // if that fails, allocate directly from the OS + td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &_mi_stats_main); + if (td == NULL) { + // if this fails, try once more. (issue #257) + td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &_mi_stats_main); + if (td == NULL) { + // really out of memory + _mi_error_message(ENOMEM, "unable to allocate thread local heap metadata (%zu bytes)\n", sizeof(mi_thread_data_t)); + } + } + return td; +} + +static void mi_thread_data_free( mi_thread_data_t* tdfree ) { + // try to add the thread metadata to the cache + for (int i = 0; i < TD_CACHE_SIZE; i++) { + mi_thread_data_t* td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]); + if (td == NULL) { + mi_thread_data_t* expected = NULL; + if (mi_atomic_cas_ptr_weak_acq_rel(mi_thread_data_t, &td_cache[i], &expected, tdfree)) { + return; + } + } + } + // if that fails, just free it directly + _mi_os_free(tdfree, sizeof(mi_thread_data_t), &_mi_stats_main); +} + +static void mi_thread_data_collect(void) { + // free all thread metadata from the cache + for (int i = 0; i < TD_CACHE_SIZE; i++) { + mi_thread_data_t* td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]); + if (td != NULL) { + td = mi_atomic_exchange_ptr_acq_rel(mi_thread_data_t, &td_cache[i], NULL); + if (td != NULL) { + _mi_os_free( td, sizeof(mi_thread_data_t), &_mi_stats_main ); + } + } + } +} + // Initialize the thread local default heap, called from `mi_thread_init` static bool _mi_heap_init(void) { - if (mi_heap_is_initialized(mi_get_default_heap())) return true; + if (mi_heap_is_initialized(mi_prim_get_default_heap())) return true; if (_mi_is_main_thread()) { // mi_assert_internal(_mi_heap_main.thread_id != 0); // can happen on freeBSD where alloc is called before any initialization // the main heap is statically allocated mi_heap_main_init(); _mi_heap_set_default_direct(&_mi_heap_main); - //mi_assert_internal(_mi_heap_default->tld->heap_backing == mi_get_default_heap()); + //mi_assert_internal(_mi_heap_default->tld->heap_backing == mi_prim_get_default_heap()); } else { // use `_mi_os_alloc` to allocate directly from the OS - mi_thread_data_t* td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &_mi_stats_main); // Todo: more efficient allocation? - if (td == NULL) { - // if this fails, try once more. (issue #257) - td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &_mi_stats_main); - if (td == NULL) { - // really out of memory - _mi_error_message(ENOMEM, "unable to allocate thread local heap metadata (%zu bytes)\n", sizeof(mi_thread_data_t)); - return false; - } - } + mi_thread_data_t* td = mi_thread_data_alloc(); + if (td == NULL) return false; + // OS allocated so already zero initialized mi_tld_t* tld = &td->tld; mi_heap_t* heap = &td->heap; + _mi_memcpy_aligned(tld, &tld_empty, sizeof(*tld)); _mi_memcpy_aligned(heap, &_mi_heap_empty, sizeof(*heap)); heap->thread_id = _mi_thread_id(); _mi_random_init(&heap->random); @@ -201,7 +294,7 @@ static bool _mi_heap_init(void) { tld->segments.stats = &tld->stats; tld->segments.os = &tld->os; tld->os.stats = &tld->stats; - _mi_heap_set_default_direct(heap); + _mi_heap_set_default_direct(heap); } return false; } @@ -234,23 +327,27 @@ static bool _mi_heap_done(mi_heap_t* heap) { if (heap != &_mi_heap_main) { _mi_heap_collect_abandon(heap); } - + // merge stats - _mi_stats_done(&heap->tld->stats); + _mi_stats_done(&heap->tld->stats); // free if not the main thread if (heap != &_mi_heap_main) { - mi_assert_internal(heap->tld->segments.count == 0 || heap->thread_id != _mi_thread_id()); - _mi_os_free(heap, sizeof(mi_thread_data_t), &_mi_stats_main); + // the following assertion does not always hold for huge segments as those are always treated + // as abondened: one may allocate it in one thread, but deallocate in another in which case + // the count can be too large or negative. todo: perhaps not count huge segments? see issue #363 + // mi_assert_internal(heap->tld->segments.count == 0 || heap->thread_id != _mi_thread_id()); + mi_thread_data_free((mi_thread_data_t*)heap); } -#if 0 - // never free the main thread even in debug mode; if a dll is linked statically with mimalloc, - // there may still be delete/free calls after the mi_fls_done is called. Issue #207 else { + mi_thread_data_collect(); // free cached thread metadata + #if 0 + // never free the main thread even in debug mode; if a dll is linked statically with mimalloc, + // there may still be delete/free calls after the mi_fls_done is called. Issue #207 _mi_heap_destroy_pages(heap); mi_assert_internal(heap->tld->heap_backing == &_mi_heap_main); + #endif } -#endif return false; } @@ -272,57 +369,12 @@ static bool _mi_heap_done(mi_heap_t* heap) { // to set up the thread local keys. // -------------------------------------------------------- -static void _mi_thread_done(mi_heap_t* default_heap); - -#ifdef __wasi__ -// no pthreads in the WebAssembly Standard Interface -#elif !defined(_WIN32) -#define MI_USE_PTHREADS -#endif - -#if defined(_WIN32) && defined(MI_SHARED_LIB) - // nothing to do as it is done in DllMain -#elif defined(_WIN32) && !defined(MI_SHARED_LIB) - // use thread local storage keys to detect thread ending - #include - #include - #if (_WIN32_WINNT < 0x600) // before Windows Vista - WINBASEAPI DWORD WINAPI FlsAlloc( _In_opt_ PFLS_CALLBACK_FUNCTION lpCallback ); - WINBASEAPI PVOID WINAPI FlsGetValue( _In_ DWORD dwFlsIndex ); - WINBASEAPI BOOL WINAPI FlsSetValue( _In_ DWORD dwFlsIndex, _In_opt_ PVOID lpFlsData ); - WINBASEAPI BOOL WINAPI FlsFree(_In_ DWORD dwFlsIndex); - #endif - static DWORD mi_fls_key = (DWORD)(-1); - static void NTAPI mi_fls_done(PVOID value) { - if (value!=NULL) _mi_thread_done((mi_heap_t*)value); - } -#elif defined(MI_USE_PTHREADS) - // use pthread local storage keys to detect thread ending - // (and used with MI_TLS_PTHREADS for the default heap) - #include - pthread_key_t _mi_heap_default_key = (pthread_key_t)(-1); - static void mi_pthread_done(void* value) { - if (value!=NULL) _mi_thread_done((mi_heap_t*)value); - } -#elif defined(__wasi__) -// no pthreads in the WebAssembly Standard Interface -#else - #pragma message("define a way to call mi_thread_done when a thread is done") -#endif - // Set up handlers so `mi_thread_done` is called automatically static void mi_process_setup_auto_thread_done(void) { static bool tls_initialized = false; // fine if it races if (tls_initialized) return; tls_initialized = true; - #if defined(_WIN32) && defined(MI_SHARED_LIB) - // nothing to do as it is done in DllMain - #elif defined(_WIN32) && !defined(MI_SHARED_LIB) - mi_fls_key = FlsAlloc(&mi_fls_done); - #elif defined(MI_USE_PTHREADS) - mi_assert_internal(_mi_heap_default_key == (pthread_key_t)(-1)); - pthread_key_create(&_mi_heap_default_key, &mi_pthread_done); - #endif + _mi_prim_thread_init_auto_done(); _mi_heap_set_default_direct(&_mi_heap_main); } @@ -331,31 +383,52 @@ bool _mi_is_main_thread(void) { return (_mi_heap_main.thread_id==0 || _mi_heap_main.thread_id == _mi_thread_id()); } +static _Atomic(size_t) thread_count = MI_ATOMIC_VAR_INIT(1); + +size_t _mi_current_thread_count(void) { + return mi_atomic_load_relaxed(&thread_count); +} + // This is called from the `mi_malloc_generic` void mi_thread_init(void) mi_attr_noexcept { // ensure our process has started already mi_process_init(); - + // initialize the thread local default heap // (this will call `_mi_heap_set_default_direct` and thus set the // fiber/pthread key to a non-zero value, ensuring `_mi_thread_done` is called) if (_mi_heap_init()) return; // returns true if already initialized _mi_stat_increase(&_mi_stats_main.threads, 1); + mi_atomic_increment_relaxed(&thread_count); //_mi_verbose_message("thread init: 0x%zx\n", _mi_thread_id()); } void mi_thread_done(void) mi_attr_noexcept { - _mi_thread_done(mi_get_default_heap()); + _mi_thread_done(NULL); } -static void _mi_thread_done(mi_heap_t* heap) { - _mi_stat_decrease(&_mi_stats_main.threads, 1); +void _mi_thread_done(mi_heap_t* heap) +{ + // calling with NULL implies using the default heap + if (heap == NULL) { + heap = mi_prim_get_default_heap(); + if (heap == NULL) return; + } + // prevent re-entrancy through heap_done/heap_set_default_direct (issue #699) + if (!mi_heap_is_initialized(heap)) { + return; + } + + // adjust stats + mi_atomic_decrement_relaxed(&thread_count); + _mi_stat_decrease(&_mi_stats_main.threads, 1); + // check thread-id as on Windows shutdown with FLS the main (exit) thread may call this on thread-local heaps... if (heap->thread_id != _mi_thread_id()) return; - + // abandon the thread local heap if (_mi_heap_done(heap)) return; // returns true if already ran } @@ -363,7 +436,7 @@ static void _mi_thread_done(mi_heap_t* heap) { void _mi_heap_set_default_direct(mi_heap_t* heap) { mi_assert_internal(heap != NULL); #if defined(MI_TLS_SLOT) - mi_tls_slot_set(MI_TLS_SLOT,heap); + mi_prim_tls_slot_set(MI_TLS_SLOT,heap); #elif defined(MI_TLS_PTHREAD_SLOT_OFS) *mi_tls_pthread_heap_slot() = heap; #elif defined(MI_TLS_PTHREAD) @@ -374,38 +447,29 @@ void _mi_heap_set_default_direct(mi_heap_t* heap) { // ensure the default heap is passed to `_mi_thread_done` // setting to a non-NULL value also ensures `mi_thread_done` is called. - #if defined(_WIN32) && defined(MI_SHARED_LIB) - // nothing to do as it is done in DllMain - #elif defined(_WIN32) && !defined(MI_SHARED_LIB) - mi_assert_internal(mi_fls_key != 0); - FlsSetValue(mi_fls_key, heap); - #elif defined(MI_USE_PTHREADS) - if (_mi_heap_default_key != (pthread_key_t)(-1)) { // can happen during recursive invocation on freeBSD - pthread_setspecific(_mi_heap_default_key, heap); - } - #endif + _mi_prim_thread_associate_default_heap(heap); } // -------------------------------------------------------- // Run functions on process init/done, and thread init/done // -------------------------------------------------------- -static void mi_process_done(void); +static void mi_cdecl mi_process_done(void); static bool os_preloading = true; // true until this module is initialized static bool mi_redirected = false; // true if malloc redirects to mi_malloc // Returns true if this module has not been initialized; Don't use C runtime routines until it returns false. -bool _mi_preloading(void) { +bool mi_decl_noinline _mi_preloading(void) { return os_preloading; } -bool mi_is_redirected(void) mi_attr_noexcept { +mi_decl_nodiscard bool mi_is_redirected(void) mi_attr_noexcept { return mi_redirected; } // Communicate with the redirection module on Windows -#if defined(_WIN32) && defined(MI_SHARED_LIB) +#if defined(_WIN32) && defined(MI_SHARED_LIB) && !defined(MI_WIN_NOREDIRECT) #ifdef __cplusplus extern "C" { #endif @@ -421,8 +485,8 @@ mi_decl_export void _mi_redirect_entry(DWORD reason) { mi_thread_done(); } } -__declspec(dllimport) bool mi_allocator_init(const char** message); -__declspec(dllimport) void mi_allocator_done(void); +__declspec(dllimport) bool mi_cdecl mi_allocator_init(const char** message); +__declspec(dllimport) void mi_cdecl mi_allocator_done(void); #ifdef __cplusplus } #endif @@ -439,15 +503,18 @@ static void mi_allocator_done(void) { // Called once by the process loader static void mi_process_load(void) { mi_heap_main_init(); - #if defined(MI_TLS_RECURSE_GUARD) + #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD) volatile mi_heap_t* dummy = _mi_heap_default; // access TLS to allocate it before setting tls_initialized to true; - UNUSED(dummy); + if (dummy == NULL) return; // use dummy or otherwise the access may get optimized away (issue #697) #endif os_preloading = false; + mi_assert_internal(_mi_is_main_thread()); + #if !(defined(_WIN32) && defined(MI_SHARED_LIB)) // use Dll process detach (see below) instead of atexit (issue #521) atexit(&mi_process_done); + #endif _mi_options_init(); + mi_process_setup_auto_thread_done(); mi_process_init(); - //mi_stats_reset();- if (mi_redirected) _mi_verbose_message("malloc is redirected.\n"); // show message from the redirector (if present) @@ -456,6 +523,9 @@ static void mi_process_load(void) { if (msg != NULL && (mi_option_is_enabled(mi_option_verbose) || mi_option_is_enabled(mi_option_show_errors))) { _mi_fputs(NULL,NULL,NULL,msg); } + + // reseed random + _mi_random_reinit_if_weak(&_mi_heap_main.random); } #if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) @@ -466,7 +536,7 @@ static void mi_detect_cpu_features(void) { // FSRM for fast rep movsb support (AMD Zen3+ (~2020) or Intel Ice Lake+ (~2017)) int32_t cpu_info[4]; __cpuid(cpu_info, 7); - _mi_cpu_has_fsrm = ((cpu_info[3] & (1 << 4)) != 0); // bit 4 of EDX : see + _mi_cpu_has_fsrm = ((cpu_info[3] & (1 << 4)) != 0); // bit 4 of EDX : see } #else static void mi_detect_cpu_features(void) { @@ -477,33 +547,54 @@ static void mi_detect_cpu_features(void) { // Initialize the process; called by thread_init or the process loader void mi_process_init(void) mi_attr_noexcept { // ensure we are called once - if (_mi_process_is_initialized) return; + static mi_atomic_once_t process_init; + if (!mi_atomic_once(&process_init)) return; _mi_process_is_initialized = true; + _mi_verbose_message("process init: 0x%zx\n", _mi_thread_id()); mi_process_setup_auto_thread_done(); - _mi_verbose_message("process init: 0x%zx\n", _mi_thread_id()); mi_detect_cpu_features(); _mi_os_init(); mi_heap_main_init(); - #if (MI_DEBUG) + #if MI_DEBUG _mi_verbose_message("debug level : %d\n", MI_DEBUG); #endif _mi_verbose_message("secure level: %d\n", MI_SECURE); + _mi_verbose_message("mem tracking: %s\n", MI_TRACK_TOOL); + #if MI_TSAN + _mi_verbose_message("thread santizer enabled\n"); + #endif mi_thread_init(); + + #if defined(_WIN32) + // On windows, when building as a static lib the FLS cleanup happens to early for the main thread. + // To avoid this, set the FLS value for the main thread to NULL so the fls cleanup + // will not call _mi_thread_done on the (still executing) main thread. See issue #508. + _mi_prim_thread_associate_default_heap(NULL); + #endif + mi_stats_reset(); // only call stat reset *after* thread init (or the heap tld == NULL) + mi_track_init(); if (mi_option_is_enabled(mi_option_reserve_huge_os_pages)) { - size_t pages = mi_option_get(mi_option_reserve_huge_os_pages); - mi_reserve_huge_os_pages_interleave(pages, 0, pages*500); - } + size_t pages = mi_option_get_clamp(mi_option_reserve_huge_os_pages, 0, 128*1024); + long reserve_at = mi_option_get(mi_option_reserve_huge_os_pages_at); + if (reserve_at != -1) { + mi_reserve_huge_os_pages_at(pages, reserve_at, pages*500); + } else { + mi_reserve_huge_os_pages_interleave(pages, 0, pages*500); + } + } if (mi_option_is_enabled(mi_option_reserve_os_memory)) { long ksize = mi_option_get(mi_option_reserve_os_memory); - if (ksize > 0) mi_reserve_os_memory((size_t)ksize*KiB, true, true); + if (ksize > 0) { + mi_reserve_os_memory((size_t)ksize*MI_KiB, true /* commit? */, true /* allow large pages? */); + } } } // Called when the process is done (through `at_exit`) -static void mi_process_done(void) { +static void mi_cdecl mi_process_done(void) { // only shutdown if we were initialized if (!_mi_process_is_initialized) return; // ensure we are called once @@ -511,22 +602,30 @@ static void mi_process_done(void) { if (process_done) return; process_done = true; - #if defined(_WIN32) && !defined(MI_SHARED_LIB) - FlsSetValue(mi_fls_key, NULL); // don't call main-thread callback - FlsFree(mi_fls_key); // call thread-done on all threads to prevent dangling callback pointer if statically linked with a DLL; Issue #208 - #endif + // release any thread specific resources and ensure _mi_thread_done is called on all but the main thread + _mi_prim_thread_done_auto_done(); - #if (MI_DEBUG != 0) || !defined(MI_SHARED_LIB) - // free all memory if possible on process exit. This is not needed for a stand-alone process - // but should be done if mimalloc is statically linked into another shared library which - // is repeatedly loaded/unloaded, see issue #281. - mi_collect(true /* force */ ); + #ifndef MI_SKIP_COLLECT_ON_EXIT + #if (MI_DEBUG != 0) || !defined(MI_SHARED_LIB) + // free all memory if possible on process exit. This is not needed for a stand-alone process + // but should be done if mimalloc is statically linked into another shared library which + // is repeatedly loaded/unloaded, see issue #281. + mi_collect(true /* force */ ); + #endif #endif + // Forcefully release all retained memory; this can be dangerous in general if overriding regular malloc/free + // since after process_done there might still be other code running that calls `free` (like at_exit routines, + // or C-runtime termination code. + if (mi_option_is_enabled(mi_option_destroy_on_exit)) { + _mi_heap_destroy_all(); // forcefully release all memory held by all heaps (of this thread only!) + _mi_segment_cache_free_all(&_mi_heap_main_get()->tld->os); // release all cached segments + } + if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) { mi_stats_print(NULL); } - mi_allocator_done(); + mi_allocator_done(); _mi_verbose_message("process done: 0x%zx\n", _mi_heap_main.thread_id); os_preloading = true; // don't call the C runtime anymore } @@ -536,31 +635,22 @@ static void mi_process_done(void) { #if defined(_WIN32) && defined(MI_SHARED_LIB) // Windows DLL: easy to hook into process_init and thread_done __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) { - UNUSED(reserved); - UNUSED(inst); + MI_UNUSED(reserved); + MI_UNUSED(inst); if (reason==DLL_PROCESS_ATTACH) { mi_process_load(); } + else if (reason==DLL_PROCESS_DETACH) { + mi_process_done(); + } else if (reason==DLL_THREAD_DETACH) { - if (!mi_is_redirected()) mi_thread_done(); + if (!mi_is_redirected()) { + mi_thread_done(); + } } return TRUE; } -#elif defined(__cplusplus) - // C++: use static initialization to detect process start - static bool _mi_process_init(void) { - mi_process_load(); - return (_mi_heap_main.thread_id != 0); - } - static bool mi_initialized = _mi_process_init(); - -#elif defined(__GNUC__) || defined(__clang__) - // GCC,Clang: use the constructor attribute - static void __attribute__((constructor)) _mi_process_init(void) { - mi_process_load(); - } - #elif defined(_MSC_VER) // MSVC: use data section magic for static libraries // See @@ -568,17 +658,31 @@ static void mi_process_done(void) { mi_process_load(); return 0; } - typedef int(*_crt_cb)(void); - #ifdef _M_X64 + typedef int(*_mi_crt_callback_t)(void); + #if defined(_M_X64) || defined(_M_ARM64) __pragma(comment(linker, "/include:" "_mi_msvc_initu")) #pragma section(".CRT$XIU", long, read) #else __pragma(comment(linker, "/include:" "__mi_msvc_initu")) #endif #pragma data_seg(".CRT$XIU") - _crt_cb _mi_msvc_initu[] = { &_mi_process_init }; + mi_decl_externc _mi_crt_callback_t _mi_msvc_initu[] = { &_mi_process_init }; #pragma data_seg() +#elif defined(__cplusplus) + // C++: use static initialization to detect process start + static bool _mi_process_init(void) { + mi_process_load(); + return (_mi_heap_main.thread_id != 0); + } + static bool mi_initialized = _mi_process_init(); + +#elif defined(__GNUC__) || defined(__clang__) + // GCC,Clang: use the constructor attribute + static void __attribute__((constructor)) _mi_process_init(void) { + mi_process_load(); + } + #else #pragma message("define a way to call mi_process_load on your platform") #endif diff --git a/Source/mimalloc/src/options.c b/Source/mimalloc/src/options.c index 30025db22..450bc2f3f 100644 --- a/Source/mimalloc/src/options.c +++ b/Source/mimalloc/src/options.c @@ -5,32 +5,24 @@ terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ #include "mimalloc.h" -#include "mimalloc-internal.h" -#include "mimalloc-atomic.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" +#include "mimalloc/prim.h" // mi_prim_out_stderr -#include -#include // strtol -#include // strncpy, strncat, strlen, strstr -#include // toupper +#include // FILE +#include // abort #include -#ifdef _MSC_VER -#pragma warning(disable:4996) // strncpy, strncat -#endif - -static uintptr_t mi_max_error_count = 16; // stop outputting errors after this -static uintptr_t mi_max_warning_count = 16; // stop outputting warnings after this +static long mi_max_error_count = 16; // stop outputting errors after this (use < 0 for no limit) +static long mi_max_warning_count = 16; // stop outputting warnings after this (use < 0 for no limit) -static void mi_add_stderr_output(); +static void mi_add_stderr_output(void); int mi_version(void) mi_attr_noexcept { return MI_MALLOC_VERSION; } -#ifdef _WIN32 -#include -#endif // -------------------------------------------------------- // Options @@ -49,50 +41,53 @@ typedef struct mi_option_desc_s { mi_init_t init; // is it initialized yet? (from the environment) mi_option_t option; // for debugging: the option index should match the option const char* name; // option name without `mimalloc_` prefix + const char* legacy_name; // potential legacy v1.x option name } mi_option_desc_t; -#define MI_OPTION(opt) mi_option_##opt, #opt -#define MI_OPTION_DESC(opt) {0, UNINIT, MI_OPTION(opt) } +#define MI_OPTION(opt) mi_option_##opt, #opt, NULL +#define MI_OPTION_LEGACY(opt,legacy) mi_option_##opt, #opt, #legacy static mi_option_desc_t options[_mi_option_last] = { // stable options -#if MI_DEBUG || defined(MI_SHOW_ERRORS) + #if MI_DEBUG || defined(MI_SHOW_ERRORS) { 1, UNINIT, MI_OPTION(show_errors) }, -#else + #else { 0, UNINIT, MI_OPTION(show_errors) }, -#endif + #endif { 0, UNINIT, MI_OPTION(show_stats) }, { 0, UNINIT, MI_OPTION(verbose) }, - // the following options are experimental and not all combinations make sense. - { 1, UNINIT, MI_OPTION(eager_commit) }, // commit per segment directly (4MiB) (but see also `eager_commit_delay`) - #if defined(_WIN32) || (MI_INTPTR_SIZE <= 4) // and other OS's without overcommit? - { 0, UNINIT, MI_OPTION(eager_region_commit) }, - { 1, UNINIT, MI_OPTION(reset_decommits) }, // reset decommits memory - #else - { 1, UNINIT, MI_OPTION(eager_region_commit) }, - { 0, UNINIT, MI_OPTION(reset_decommits) }, // reset uses MADV_FREE/MADV_DONTNEED - #endif + // Some of the following options are experimental and not all combinations are valid. Use with care. + { 1, UNINIT, MI_OPTION(eager_commit) }, // commit per segment directly (8MiB) (but see also `eager_commit_delay`) + { 0, UNINIT, MI_OPTION(deprecated_eager_region_commit) }, + { 0, UNINIT, MI_OPTION(deprecated_reset_decommits) }, { 0, UNINIT, MI_OPTION(large_os_pages) }, // use large OS pages, use only with eager commit to prevent fragmentation of VMA's { 0, UNINIT, MI_OPTION(reserve_huge_os_pages) }, // per 1GiB huge pages + { -1, UNINIT, MI_OPTION(reserve_huge_os_pages_at) }, // reserve huge pages at node N { 0, UNINIT, MI_OPTION(reserve_os_memory) }, - { 0, UNINIT, MI_OPTION(segment_cache) }, // cache N segments per thread - { 1, UNINIT, MI_OPTION(page_reset) }, // reset page memory on free - { 0, UNINIT, MI_OPTION(abandoned_page_reset) },// reset free page memory when a thread terminates - { 0, UNINIT, MI_OPTION(segment_reset) }, // reset segment memory on free (needs eager commit) -#if defined(__NetBSD__) + { 0, UNINIT, MI_OPTION(deprecated_segment_cache) }, // cache N segments per thread + { 0, UNINIT, MI_OPTION(page_reset) }, // reset page memory on free + { 0, UNINIT, MI_OPTION_LEGACY(abandoned_page_decommit, abandoned_page_reset) },// decommit free page memory when a thread terminates + { 0, UNINIT, MI_OPTION(deprecated_segment_reset) }, + #if defined(__NetBSD__) { 0, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed -#else + #elif defined(_WIN32) + { 4, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed (but per page in the segment on demand) + #else { 1, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed (but per page in the segment on demand) -#endif - { 100, UNINIT, MI_OPTION(reset_delay) }, // reset delay in milli-seconds - { 0, UNINIT, MI_OPTION(use_numa_nodes) }, // 0 = use available numa nodes, otherwise use at most N nodes. - { 0, UNINIT, MI_OPTION(limit_os_alloc) }, // 1 = do not use OS memory for allocation (but only reserved arenas) - { 100, UNINIT, MI_OPTION(os_tag) }, // only apple specific for now but might serve more or less related purpose - { 16, UNINIT, MI_OPTION(max_errors) }, // maximum errors that are output - { 16, UNINIT, MI_OPTION(max_warnings) } // maximum warnings that are output - + #endif + { 25, UNINIT, MI_OPTION_LEGACY(decommit_delay, reset_delay) }, // page decommit delay in milli-seconds + { 0, UNINIT, MI_OPTION(use_numa_nodes) }, // 0 = use available numa nodes, otherwise use at most N nodes. + { 0, UNINIT, MI_OPTION(limit_os_alloc) }, // 1 = do not use OS memory for allocation (but only reserved arenas) + { 100, UNINIT, MI_OPTION(os_tag) }, // only apple specific for now but might serve more or less related purpose + { 16, UNINIT, MI_OPTION(max_errors) }, // maximum errors that are output + { 16, UNINIT, MI_OPTION(max_warnings) }, // maximum warnings that are output + { 8, UNINIT, MI_OPTION(max_segment_reclaim)},// max. number of segment reclaims from the abandoned segments per try. + { 1, UNINIT, MI_OPTION(allow_decommit) }, // decommit slices when no longer used (after decommit_delay milli-seconds) + { 500, UNINIT, MI_OPTION(segment_decommit_delay) }, // decommit delay in milli-seconds for freed segments + { 1, UNINIT, MI_OPTION(decommit_extend_delay) }, + { 0, UNINIT, MI_OPTION(destroy_on_exit)} // release all OS memory on process exit; careful with dangling pointer or after-exit frees! }; static void mi_option_init(mi_option_desc_t* desc); @@ -103,8 +98,9 @@ void _mi_options_init(void) { mi_add_stderr_output(); // now it safe to use stderr for output for(int i = 0; i < _mi_option_last; i++ ) { mi_option_t option = (mi_option_t)i; - long l = mi_option_get(option); UNUSED(l); // initialize - if (option != mi_option_verbose) { + long l = mi_option_get(option); MI_UNUSED(l); // initialize + // if (option != mi_option_verbose) + { mi_option_desc_t* desc = &options[option]; _mi_verbose_message("option '%s': %ld\n", desc->name, desc->value); } @@ -113,18 +109,25 @@ void _mi_options_init(void) { mi_max_warning_count = mi_option_get(mi_option_max_warnings); } -long mi_option_get(mi_option_t option) { +mi_decl_nodiscard long mi_option_get(mi_option_t option) { mi_assert(option >= 0 && option < _mi_option_last); + if (option < 0 || option >= _mi_option_last) return 0; mi_option_desc_t* desc = &options[option]; mi_assert(desc->option == option); // index should match the option - if (mi_unlikely(desc->init == UNINIT)) { + if mi_unlikely(desc->init == UNINIT) { mi_option_init(desc); } return desc->value; } +mi_decl_nodiscard long mi_option_get_clamp(mi_option_t option, long min, long max) { + long x = mi_option_get(option); + return (x < min ? min : (x > max ? max : x)); +} + void mi_option_set(mi_option_t option, long value) { mi_assert(option >= 0 && option < _mi_option_last); + if (option < 0 || option >= _mi_option_last) return; mi_option_desc_t* desc = &options[option]; mi_assert(desc->option == option); // index should match the option desc->value = value; @@ -133,13 +136,14 @@ void mi_option_set(mi_option_t option, long value) { void mi_option_set_default(mi_option_t option, long value) { mi_assert(option >= 0 && option < _mi_option_last); + if (option < 0 || option >= _mi_option_last) return; mi_option_desc_t* desc = &options[option]; if (desc->init != INITIALIZED) { desc->value = value; } } -bool mi_option_is_enabled(mi_option_t option) { +mi_decl_nodiscard bool mi_option_is_enabled(mi_option_t option) { return (mi_option_get(option) != 0); } @@ -159,16 +163,11 @@ void mi_option_disable(mi_option_t option) { mi_option_set_enabled(option,false); } - -static void mi_out_stderr(const char* msg, void* arg) { - UNUSED(arg); - #ifdef _WIN32 - // on windows with redirection, the C runtime cannot handle locale dependent output - // after the main thread closes so we use direct console output. - if (!_mi_preloading()) { _cputs(msg); } - #else - fputs(msg, stderr); - #endif +static void mi_cdecl mi_out_stderr(const char* msg, void* arg) { + MI_UNUSED(arg); + if (msg != NULL && msg[0] != 0) { + _mi_prim_out_stderr(msg); + } } // Since an output function can be registered earliest in the `main` @@ -176,19 +175,19 @@ static void mi_out_stderr(const char* msg, void* arg) { // an output function is registered it is called immediately with // the output up to that point. #ifndef MI_MAX_DELAY_OUTPUT -#define MI_MAX_DELAY_OUTPUT ((uintptr_t)(32*1024)) +#define MI_MAX_DELAY_OUTPUT ((size_t)(32*1024)) #endif static char out_buf[MI_MAX_DELAY_OUTPUT+1]; -static _Atomic(uintptr_t) out_len; +static _Atomic(size_t) out_len; -static void mi_out_buf(const char* msg, void* arg) { - UNUSED(arg); +static void mi_cdecl mi_out_buf(const char* msg, void* arg) { + MI_UNUSED(arg); if (msg==NULL) return; if (mi_atomic_load_relaxed(&out_len)>=MI_MAX_DELAY_OUTPUT) return; - size_t n = strlen(msg); + size_t n = _mi_strlen(msg); if (n==0) return; // claim space - uintptr_t start = mi_atomic_add_acq_rel(&out_len, n); + size_t start = mi_atomic_add_acq_rel(&out_len, n); if (start >= MI_MAX_DELAY_OUTPUT) return; // check bound if (start+n >= MI_MAX_DELAY_OUTPUT) { @@ -213,7 +212,7 @@ static void mi_out_buf_flush(mi_output_fun* out, bool no_more_buf, void* arg) { // Once this module is loaded, switch to this routine // which outputs to stderr and the delayed output buffer. -static void mi_out_buf_stderr(const char* msg, void* arg) { +static void mi_cdecl mi_out_buf_stderr(const char* msg, void* arg) { mi_out_stderr(msg,arg); mi_out_buf(msg,arg); } @@ -251,31 +250,46 @@ static void mi_add_stderr_output() { // -------------------------------------------------------- // Messages, all end up calling `_mi_fputs`. // -------------------------------------------------------- -static _Atomic(uintptr_t) error_count; // = 0; // when >= max_error_count stop emitting errors -static _Atomic(uintptr_t) warning_count; // = 0; // when >= max_warning_count stop emitting warnings +static _Atomic(size_t) error_count; // = 0; // when >= max_error_count stop emitting errors +static _Atomic(size_t) warning_count; // = 0; // when >= max_warning_count stop emitting warnings // When overriding malloc, we may recurse into mi_vfprintf if an allocation // inside the C runtime causes another message. +// In some cases (like on macOS) the loader already allocates which +// calls into mimalloc; if we then access thread locals (like `recurse`) +// this may crash as the access may call _tlv_bootstrap that tries to +// (recursively) invoke malloc again to allocate space for the thread local +// variables on demand. This is why we use a _mi_preloading test on such +// platforms. However, C code generator may move the initial thread local address +// load before the `if` and we therefore split it out in a separate funcion. static mi_decl_thread bool recurse = false; -static bool mi_recurse_enter(void) { - #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD) - if (_mi_preloading()) return true; - #endif +static mi_decl_noinline bool mi_recurse_enter_prim(void) { if (recurse) return false; recurse = true; return true; } +static mi_decl_noinline void mi_recurse_exit_prim(void) { + recurse = false; +} + +static bool mi_recurse_enter(void) { + #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD) + if (_mi_preloading()) return false; + #endif + return mi_recurse_enter_prim(); +} + static void mi_recurse_exit(void) { #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD) if (_mi_preloading()) return; #endif - recurse = false; + mi_recurse_exit_prim(); } void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message) { - if (out==NULL || (FILE*)out==stdout || (FILE*)out==stderr) { // TODO: use mi_out_stderr for stderr? + if (out==NULL || (void*)out==(void*)stdout || (void*)out==(void*)stderr) { // TODO: use mi_out_stderr for stderr? if (!mi_recurse_enter()) return; out = mi_out_get_default(&arg); if (prefix != NULL) out(prefix, arg); @@ -306,11 +320,22 @@ void _mi_fprintf( mi_output_fun* out, void* arg, const char* fmt, ... ) { va_end(args); } +static void mi_vfprintf_thread(mi_output_fun* out, void* arg, const char* prefix, const char* fmt, va_list args) { + if (prefix != NULL && _mi_strnlen(prefix,33) <= 32 && !_mi_is_main_thread()) { + char tprefix[64]; + snprintf(tprefix, sizeof(tprefix), "%sthread 0x%llx: ", prefix, (unsigned long long)_mi_thread_id()); + mi_vfprintf(out, arg, tprefix, fmt, args); + } + else { + mi_vfprintf(out, arg, prefix, fmt, args); + } +} + void _mi_trace_message(const char* fmt, ...) { if (mi_option_get(mi_option_verbose) <= 1) return; // only with verbose level 2 or higher va_list args; va_start(args, fmt); - mi_vfprintf(NULL, NULL, "mimalloc: ", fmt, args); + mi_vfprintf_thread(NULL, NULL, "mimalloc: ", fmt, args); va_end(args); } @@ -323,17 +348,21 @@ void _mi_verbose_message(const char* fmt, ...) { } static void mi_show_error_message(const char* fmt, va_list args) { - if (!mi_option_is_enabled(mi_option_show_errors) && !mi_option_is_enabled(mi_option_verbose)) return; - if (mi_atomic_increment_acq_rel(&error_count) > mi_max_error_count) return; - mi_vfprintf(NULL, NULL, "mimalloc: error: ", fmt, args); + if (!mi_option_is_enabled(mi_option_verbose)) { + if (!mi_option_is_enabled(mi_option_show_errors)) return; + if (mi_max_error_count >= 0 && (long)mi_atomic_increment_acq_rel(&error_count) > mi_max_error_count) return; + } + mi_vfprintf_thread(NULL, NULL, "mimalloc: error: ", fmt, args); } void _mi_warning_message(const char* fmt, ...) { - if (!mi_option_is_enabled(mi_option_show_errors) && !mi_option_is_enabled(mi_option_verbose)) return; - if (mi_atomic_increment_acq_rel(&warning_count) > mi_max_warning_count) return; + if (!mi_option_is_enabled(mi_option_verbose)) { + if (!mi_option_is_enabled(mi_option_show_errors)) return; + if (mi_max_warning_count >= 0 && (long)mi_atomic_increment_acq_rel(&warning_count) > mi_max_warning_count) return; + } va_list args; va_start(args,fmt); - mi_vfprintf(NULL, NULL, "mimalloc: warning: ", fmt, args); + mi_vfprintf_thread(NULL, NULL, "mimalloc: warning: ", fmt, args); va_end(args); } @@ -353,8 +382,8 @@ static mi_error_fun* volatile mi_error_handler; // = NULL static _Atomic(void*) mi_error_arg; // = NULL static void mi_error_default(int err) { - UNUSED(err); -#if (MI_DEBUG>0) + MI_UNUSED(err); +#if (MI_DEBUG>0) if (err==EFAULT) { #ifdef _MSC_VER __debugbreak(); @@ -397,106 +426,94 @@ void _mi_error_message(int err, const char* fmt, ...) { // -------------------------------------------------------- // Initialize options by checking the environment // -------------------------------------------------------- +char _mi_toupper(char c) { + if (c >= 'a' && c <= 'z') return (c - 'a' + 'A'); + else return c; +} -static void mi_strlcpy(char* dest, const char* src, size_t dest_size) { - dest[0] = 0; - strncpy(dest, src, dest_size - 1); - dest[dest_size - 1] = 0; +int _mi_strnicmp(const char* s, const char* t, size_t n) { + if (n == 0) return 0; + for (; *s != 0 && *t != 0 && n > 0; s++, t++, n--) { + if (_mi_toupper(*s) != _mi_toupper(*t)) break; + } + return (n == 0 ? 0 : *s - *t); } -static void mi_strlcat(char* dest, const char* src, size_t dest_size) { - strncat(dest, src, dest_size - 1); - dest[dest_size - 1] = 0; +void _mi_strlcpy(char* dest, const char* src, size_t dest_size) { + if (dest==NULL || src==NULL || dest_size == 0) return; + // copy until end of src, or when dest is (almost) full + while (*src != 0 && dest_size > 1) { + *dest++ = *src++; + dest_size--; + } + // always zero terminate + *dest = 0; } -static inline int mi_strnicmp(const char* s, const char* t, size_t n) { - if (n==0) return 0; - for (; *s != 0 && *t != 0 && n > 0; s++, t++, n--) { - if (toupper(*s) != toupper(*t)) break; +void _mi_strlcat(char* dest, const char* src, size_t dest_size) { + if (dest==NULL || src==NULL || dest_size == 0) return; + // find end of string in the dest buffer + while (*dest != 0 && dest_size > 1) { + dest++; + dest_size--; } - return (n==0 ? 0 : *s - *t); + // and catenate + _mi_strlcpy(dest, src, dest_size); } -#if defined _WIN32 -// On Windows use GetEnvironmentVariable instead of getenv to work -// reliably even when this is invoked before the C runtime is initialized. -// i.e. when `_mi_preloading() == true`. -// Note: on windows, environment names are not case sensitive. -#include -static bool mi_getenv(const char* name, char* result, size_t result_size) { - result[0] = 0; - size_t len = GetEnvironmentVariableA(name, result, (DWORD)result_size); - return (len > 0 && len < result_size); -} -#elif !defined(MI_USE_ENVIRON) || (MI_USE_ENVIRON!=0) -// On Posix systemsr use `environ` to acces environment variables -// even before the C runtime is initialized. -#if defined(__APPLE__) && defined(__has_include) && __has_include() -#include -static char** mi_get_environ(void) { - return (*_NSGetEnviron()); -} -#else -extern char** environ; -static char** mi_get_environ(void) { - return environ; +size_t _mi_strlen(const char* s) { + if (s==NULL) return 0; + size_t len = 0; + while(s[len] != 0) { len++; } + return len; } -#endif + +size_t _mi_strnlen(const char* s, size_t max_len) { + if (s==NULL) return 0; + size_t len = 0; + while(s[len] != 0 && len < max_len) { len++; } + return len; +} + +#ifdef MI_NO_GETENV static bool mi_getenv(const char* name, char* result, size_t result_size) { - if (name==NULL) return false; - const size_t len = strlen(name); - if (len == 0) return false; - char** env = mi_get_environ(); - if (env == NULL) return false; - // compare up to 256 entries - for (int i = 0; i < 256 && env[i] != NULL; i++) { - const char* s = env[i]; - if (mi_strnicmp(name, s, len) == 0 && s[len] == '=') { // case insensitive - // found it - mi_strlcpy(result, s + len + 1, result_size); - return true; - } - } + MI_UNUSED(name); + MI_UNUSED(result); + MI_UNUSED(result_size); return false; } -#else -// fallback: use standard C `getenv` but this cannot be used while initializing the C runtime +#else static bool mi_getenv(const char* name, char* result, size_t result_size) { - // cannot call getenv() when still initializing the C runtime. - if (_mi_preloading()) return false; - const char* s = getenv(name); - if (s == NULL) { - // we check the upper case name too. - char buf[64+1]; - size_t len = strlen(name); - if (len >= sizeof(buf)) len = sizeof(buf) - 1; - for (size_t i = 0; i < len; i++) { - buf[i] = toupper(name[i]); - } - buf[len] = 0; - s = getenv(buf); - } - if (s != NULL && strlen(s) < result_size) { - mi_strlcpy(result, s, result_size); - return true; - } - else { - return false; - } + if (name==NULL || result == NULL || result_size < 64) return false; + return _mi_prim_getenv(name,result,result_size); } #endif -static void mi_option_init(mi_option_desc_t* desc) { +// TODO: implement ourselves to reduce dependencies on the C runtime +#include // strtol +#include // strstr + + +static void mi_option_init(mi_option_desc_t* desc) { // Read option value from the environment - char buf[64+1]; - mi_strlcpy(buf, "mimalloc_", sizeof(buf)); - mi_strlcat(buf, desc->name, sizeof(buf)); char s[64+1]; - if (mi_getenv(buf, s, sizeof(s))) { - size_t len = strlen(s); - if (len >= sizeof(buf)) len = sizeof(buf) - 1; + char buf[64+1]; + _mi_strlcpy(buf, "mimalloc_", sizeof(buf)); + _mi_strlcat(buf, desc->name, sizeof(buf)); + bool found = mi_getenv(buf,s,sizeof(s)); + if (!found && desc->legacy_name != NULL) { + _mi_strlcpy(buf, "mimalloc_", sizeof(buf)); + _mi_strlcat(buf, desc->legacy_name, sizeof(buf)); + found = mi_getenv(buf,s,sizeof(s)); + if (found) { + _mi_warning_message("environment option \"mimalloc_%s\" is deprecated -- use \"mimalloc_%s\" instead.\n", desc->legacy_name, desc->name ); + } + } + + if (found) { + size_t len = _mi_strnlen(s,sizeof(buf)-1); for (size_t i = 0; i < len; i++) { - buf[i] = (char)toupper(s[i]); + buf[i] = _mi_toupper(s[i]); } buf[len] = 0; if (buf[0]==0 || strstr("1;TRUE;YES;ON", buf) != NULL) { @@ -513,18 +530,29 @@ static void mi_option_init(mi_option_desc_t* desc) { if (desc->option == mi_option_reserve_os_memory) { // this option is interpreted in KiB to prevent overflow of `long` if (*end == 'K') { end++; } - else if (*end == 'M') { value *= KiB; end++; } - else if (*end == 'G') { value *= MiB; end++; } - else { value = (value + KiB - 1) / KiB; } - if (*end == 'B') { end++; } + else if (*end == 'M') { value *= MI_KiB; end++; } + else if (*end == 'G') { value *= MI_MiB; end++; } + else { value = (value + MI_KiB - 1) / MI_KiB; } + if (end[0] == 'I' && end[1] == 'B') { end += 2; } + else if (*end == 'B') { end++; } } if (*end == 0) { desc->value = value; desc->init = INITIALIZED; } else { - _mi_warning_message("environment option mimalloc_%s has an invalid value: %s\n", desc->name, buf); + // set `init` first to avoid recursion through _mi_warning_message on mimalloc_verbose. desc->init = DEFAULTED; + if (desc->option == mi_option_verbose && desc->value == 0) { + // if the 'mimalloc_verbose' env var has a bogus value we'd never know + // (since the value defaults to 'off') so in that case briefly enable verbose + desc->value = 1; + _mi_warning_message("environment option mimalloc_%s has an invalid value.\n", desc->name ); + desc->value = 0; + } + else { + _mi_warning_message("environment option mimalloc_%s has an invalid value.\n", desc->name ); + } } } mi_assert_internal(desc->init != UNINIT); diff --git a/Source/mimalloc/src/os.c b/Source/mimalloc/src/os.c index 85415232d..6145ccb36 100644 --- a/Source/mimalloc/src/os.c +++ b/Source/mimalloc/src/os.c @@ -1,527 +1,176 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +Copyright (c) 2018-2023, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ -#ifndef _DEFAULT_SOURCE -#define _DEFAULT_SOURCE // ensure mmap flags are defined -#endif - -#if defined(__sun) -// illumos provides new mman.h api when any of these are defined -// otherwise the old api based on caddr_t which predates the void pointers one. -// stock solaris provides only the former, chose to atomically to discard those -// flags only here rather than project wide tough. -#undef _XOPEN_SOURCE -#undef _POSIX_C_SOURCE -#endif #include "mimalloc.h" -#include "mimalloc-internal.h" -#include "mimalloc-atomic.h" - -#include // strerror +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" +#include "mimalloc/prim.h" -#ifdef _MSC_VER -#pragma warning(disable:4996) // strerror -#endif - - -#if defined(_WIN32) -#include -#elif defined(__wasi__) -// stdlib.h is all we need, and has already been included in mimalloc.h -#else -#include // mmap -#include // sysconf -#if defined(__linux__) -#include -#if defined(__GLIBC__) -#include // linux mmap flags -#else -#include -#endif -#endif -#if defined(__APPLE__) -#include -#if !TARGET_IOS_IPHONE && !TARGET_IOS_SIMULATOR -#include -#endif -#endif -#if defined(__HAIKU__) -#define madvise posix_madvise -#define MADV_DONTNEED POSIX_MADV_DONTNEED -#endif -#endif /* ----------------------------------------------------------- Initialization. On windows initializes support for aligned allocation and large OS pages (if MIMALLOC_LARGE_OS_PAGES is true). ----------------------------------------------------------- */ -bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats); - -static void* mi_align_up_ptr(void* p, size_t alignment) { - return (void*)_mi_align_up((uintptr_t)p, alignment); -} -static inline uintptr_t _mi_align_down(uintptr_t sz, size_t alignment) { - mi_assert_internal(alignment != 0); - uintptr_t mask = alignment - 1; - if ((alignment & mask) == 0) { // power of two? - return (sz & ~mask); - } - else { - return ((sz / alignment) * alignment); - } -} +static mi_os_mem_config_t mi_os_mem_config = { + 4096, // page size + 0, // large page size (usually 2MiB) + 4096, // allocation granularity + true, // has overcommit? (if true we use MAP_NORESERVE on mmap systems) + false // must free whole? (on mmap systems we can free anywhere in a mapped range, but on Windows we must free the entire span) +}; -static void* mi_align_down_ptr(void* p, size_t alignment) { - return (void*)_mi_align_down((uintptr_t)p, alignment); +bool _mi_os_has_overcommit(void) { + return mi_os_mem_config.has_overcommit; } -// page size (initialized properly in `os_init`) -static size_t os_page_size = 4096; - -// minimal allocation granularity -static size_t os_alloc_granularity = 4096; - -// if non-zero, use large page allocation -static size_t large_os_page_size = 0; - // OS (small) page size -size_t _mi_os_page_size() { - return os_page_size; +size_t _mi_os_page_size(void) { + return mi_os_mem_config.page_size; } // if large OS pages are supported (2 or 4MiB), then return the size, otherwise return the small page size (4KiB) -size_t _mi_os_large_page_size() { - return (large_os_page_size != 0 ? large_os_page_size : _mi_os_page_size()); +size_t _mi_os_large_page_size(void) { + return (mi_os_mem_config.large_page_size != 0 ? mi_os_mem_config.large_page_size : _mi_os_page_size()); } -static bool use_large_os_page(size_t size, size_t alignment) { +bool _mi_os_use_large_page(size_t size, size_t alignment) { // if we have access, check the size and alignment requirements - if (large_os_page_size == 0 || !mi_option_is_enabled(mi_option_large_os_pages)) return false; - return ((size % large_os_page_size) == 0 && (alignment % large_os_page_size) == 0); + if (mi_os_mem_config.large_page_size == 0 || !mi_option_is_enabled(mi_option_large_os_pages)) return false; + return ((size % mi_os_mem_config.large_page_size) == 0 && (alignment % mi_os_mem_config.large_page_size) == 0); } // round to a good OS allocation size (bounded by max 12.5% waste) size_t _mi_os_good_alloc_size(size_t size) { size_t align_size; - if (size < 512*KiB) align_size = _mi_os_page_size(); - else if (size < 2*MiB) align_size = 64*KiB; - else if (size < 8*MiB) align_size = 256*KiB; - else if (size < 32*MiB) align_size = 1*MiB; - else align_size = 4*MiB; - if (mi_unlikely(size >= (SIZE_MAX - align_size))) return size; // possible overflow? + if (size < 512*MI_KiB) align_size = _mi_os_page_size(); + else if (size < 2*MI_MiB) align_size = 64*MI_KiB; + else if (size < 8*MI_MiB) align_size = 256*MI_KiB; + else if (size < 32*MI_MiB) align_size = 1*MI_MiB; + else align_size = 4*MI_MiB; + if mi_unlikely(size >= (SIZE_MAX - align_size)) return size; // possible overflow? return _mi_align_up(size, align_size); } -#if defined(_WIN32) -// We use VirtualAlloc2 for aligned allocation, but it is only supported on Windows 10 and Windows Server 2016. -// So, we need to look it up dynamically to run on older systems. (use __stdcall for 32-bit compatibility) -// NtAllocateVirtualAllocEx is used for huge OS page allocation (1GiB) -// -// We hide MEM_EXTENDED_PARAMETER to compile with older SDK's. -#include -typedef PVOID (__stdcall *PVirtualAlloc2)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, /* MEM_EXTENDED_PARAMETER* */ void*, ULONG); -typedef NTSTATUS (__stdcall *PNtAllocateVirtualMemoryEx)(HANDLE, PVOID*, SIZE_T*, ULONG, ULONG, /* MEM_EXTENDED_PARAMETER* */ PVOID, ULONG); -static PVirtualAlloc2 pVirtualAlloc2 = NULL; -static PNtAllocateVirtualMemoryEx pNtAllocateVirtualMemoryEx = NULL; - -// Similarly, GetNumaProcesorNodeEx is only supported since Windows 7 -#if (_WIN32_WINNT < 0x601) // before Win7 -typedef struct _PROCESSOR_NUMBER { WORD Group; BYTE Number; BYTE Reserved; } PROCESSOR_NUMBER, *PPROCESSOR_NUMBER; -#endif -typedef VOID (__stdcall *PGetCurrentProcessorNumberEx)(PPROCESSOR_NUMBER ProcNumber); -typedef BOOL (__stdcall *PGetNumaProcessorNodeEx)(PPROCESSOR_NUMBER Processor, PUSHORT NodeNumber); -typedef BOOL (__stdcall* PGetNumaNodeProcessorMaskEx)(USHORT Node, PGROUP_AFFINITY ProcessorMask); -static PGetCurrentProcessorNumberEx pGetCurrentProcessorNumberEx = NULL; -static PGetNumaProcessorNodeEx pGetNumaProcessorNodeEx = NULL; -static PGetNumaNodeProcessorMaskEx pGetNumaNodeProcessorMaskEx = NULL; - -static bool mi_win_enable_large_os_pages() -{ - if (large_os_page_size > 0) return true; - - // Try to see if large OS pages are supported - // To use large pages on Windows, we first need access permission - // Set "Lock pages in memory" permission in the group policy editor - // - unsigned long err = 0; - HANDLE token = NULL; - BOOL ok = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token); - if (ok) { - TOKEN_PRIVILEGES tp; - ok = LookupPrivilegeValue(NULL, TEXT("SeLockMemoryPrivilege"), &tp.Privileges[0].Luid); - if (ok) { - tp.PrivilegeCount = 1; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - ok = AdjustTokenPrivileges(token, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, 0); - if (ok) { - err = GetLastError(); - ok = (err == ERROR_SUCCESS); - if (ok) { - large_os_page_size = GetLargePageMinimum(); - } - } - } - CloseHandle(token); - } - if (!ok) { - if (err == 0) err = GetLastError(); - _mi_warning_message("cannot enable large OS page support, error %lu\n", err); - } - return (ok!=0); -} - void _mi_os_init(void) { - // get the page size - SYSTEM_INFO si; - GetSystemInfo(&si); - if (si.dwPageSize > 0) os_page_size = si.dwPageSize; - if (si.dwAllocationGranularity > 0) os_alloc_granularity = si.dwAllocationGranularity; - // get the VirtualAlloc2 function - HINSTANCE hDll; - hDll = LoadLibrary(TEXT("kernelbase.dll")); - if (hDll != NULL) { - // use VirtualAlloc2FromApp if possible as it is available to Windows store apps - pVirtualAlloc2 = (PVirtualAlloc2)(void (*)(void))GetProcAddress(hDll, "VirtualAlloc2FromApp"); - if (pVirtualAlloc2==NULL) pVirtualAlloc2 = (PVirtualAlloc2)(void (*)(void))GetProcAddress(hDll, "VirtualAlloc2"); - FreeLibrary(hDll); - } - // NtAllocateVirtualMemoryEx is used for huge page allocation - hDll = LoadLibrary(TEXT("ntdll.dll")); - if (hDll != NULL) { - pNtAllocateVirtualMemoryEx = (PNtAllocateVirtualMemoryEx)(void (*)(void))GetProcAddress(hDll, "NtAllocateVirtualMemoryEx"); - FreeLibrary(hDll); - } - // Try to use Win7+ numa API - hDll = LoadLibrary(TEXT("kernel32.dll")); - if (hDll != NULL) { - pGetCurrentProcessorNumberEx = (PGetCurrentProcessorNumberEx)(void (*)(void))GetProcAddress(hDll, "GetCurrentProcessorNumberEx"); - pGetNumaProcessorNodeEx = (PGetNumaProcessorNodeEx)(void (*)(void))GetProcAddress(hDll, "GetNumaProcessorNodeEx"); - pGetNumaNodeProcessorMaskEx = (PGetNumaNodeProcessorMaskEx)(void (*)(void))GetProcAddress(hDll, "GetNumaNodeProcessorMaskEx"); - FreeLibrary(hDll); - } - if (mi_option_is_enabled(mi_option_large_os_pages) || mi_option_is_enabled(mi_option_reserve_huge_os_pages)) { - mi_win_enable_large_os_pages(); - } -} -#elif defined(__wasi__) -void _mi_os_init() { - os_page_size = 0x10000; // WebAssembly has a fixed page size: 64KB - os_alloc_granularity = 16; -} -#else -void _mi_os_init() { - // get the page size - long result = sysconf(_SC_PAGESIZE); - if (result > 0) { - os_page_size = (size_t)result; - os_alloc_granularity = os_page_size; - } - large_os_page_size = 2*MiB; // TODO: can we query the OS for this? + _mi_prim_mem_init(&mi_os_mem_config); } -#endif /* ----------------------------------------------------------- - Raw allocation on Windows (VirtualAlloc) and Unix's (mmap). ------------------------------------------------------------ */ + Util +-------------------------------------------------------------- */ +bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats); +bool _mi_os_commit(void* addr, size_t size, bool* is_zero, mi_stats_t* tld_stats); -static bool mi_os_mem_free(void* addr, size_t size, bool was_committed, mi_stats_t* stats) -{ - if (addr == NULL || size == 0) return true; // || _mi_os_is_huge_reserved(addr) - bool err = false; -#if defined(_WIN32) - err = (VirtualFree(addr, 0, MEM_RELEASE) == 0); -#elif defined(__wasi__) - err = 0; // WebAssembly's heap cannot be shrunk -#else - err = (munmap(addr, size) == -1); -#endif - if (was_committed) _mi_stat_decrease(&stats->committed, size); - _mi_stat_decrease(&stats->reserved, size); - if (err) { - _mi_warning_message("munmap failed: %s, addr 0x%8li, size %lu\n", strerror(errno), (size_t)addr, size); - return false; - } - else { - return true; - } -} - -static void* mi_os_get_aligned_hint(size_t try_alignment, size_t size); - -#ifdef _WIN32 -static void* mi_win_virtual_allocx(void* addr, size_t size, size_t try_alignment, DWORD flags) { -#if (MI_INTPTR_SIZE >= 8) - // on 64-bit systems, try to use the virtual address area after 4TiB for 4MiB aligned allocations - void* hint; - if (addr == NULL && (hint = mi_os_get_aligned_hint(try_alignment,size)) != NULL) { - void* p = VirtualAlloc(hint, size, flags, PAGE_READWRITE); - if (p != NULL) return p; - DWORD err = GetLastError(); - if (err != ERROR_INVALID_ADDRESS && // If linked with multiple instances, we may have tried to allocate at an already allocated area (#210) - err != ERROR_INVALID_PARAMETER) { // Windows7 instability (#230) - return NULL; - } - // fall through - } -#endif -#if defined(MEM_EXTENDED_PARAMETER_TYPE_BITS) - // on modern Windows try use VirtualAlloc2 for aligned allocation - if (try_alignment > 0 && (try_alignment % _mi_os_page_size()) == 0 && pVirtualAlloc2 != NULL) { - MEM_ADDRESS_REQUIREMENTS reqs = { 0, 0, 0 }; - reqs.Alignment = try_alignment; - MEM_EXTENDED_PARAMETER param = { {0, 0}, {0} }; - param.Type = MemExtendedParameterAddressRequirements; - param.Pointer = &reqs; - return (*pVirtualAlloc2)(GetCurrentProcess(), addr, size, flags, PAGE_READWRITE, ¶m, 1); - } -#endif - // last resort - return VirtualAlloc(addr, size, flags, PAGE_READWRITE); +static void* mi_align_up_ptr(void* p, size_t alignment) { + return (void*)_mi_align_up((uintptr_t)p, alignment); } -static void* mi_win_virtual_alloc(void* addr, size_t size, size_t try_alignment, DWORD flags, bool large_only, bool allow_large, bool* is_large) { - mi_assert_internal(!(large_only && !allow_large)); - static _Atomic(uintptr_t) large_page_try_ok; // = 0; - void* p = NULL; - if ((large_only || use_large_os_page(size, try_alignment)) - && allow_large && (flags&MEM_COMMIT)!=0 && (flags&MEM_RESERVE)!=0) { - uintptr_t try_ok = mi_atomic_load_acquire(&large_page_try_ok); - if (!large_only && try_ok > 0) { - // if a large page allocation fails, it seems the calls to VirtualAlloc get very expensive. - // therefore, once a large page allocation failed, we don't try again for `large_page_try_ok` times. - mi_atomic_cas_strong_acq_rel(&large_page_try_ok, &try_ok, try_ok - 1); - } - else { - // large OS pages must always reserve and commit. - *is_large = true; - p = mi_win_virtual_allocx(addr, size, try_alignment, flags | MEM_LARGE_PAGES); - if (large_only) return p; - // fall back to non-large page allocation on error (`p == NULL`). - if (p == NULL) { - mi_atomic_store_release(&large_page_try_ok,10UL); // on error, don't try again for the next N allocations - } - } - } - if (p == NULL) { - *is_large = ((flags&MEM_LARGE_PAGES) != 0); - p = mi_win_virtual_allocx(addr, size, try_alignment, flags); - } - if (p == NULL) { - _mi_warning_message("unable to allocate OS memory (%zu bytes, error code: %i, address: %p, large only: %d, allow large: %d)\n", size, GetLastError(), addr, large_only, allow_large); - } - return p; +static void* mi_align_down_ptr(void* p, size_t alignment) { + return (void*)_mi_align_down((uintptr_t)p, alignment); } -#elif defined(__wasi__) -static void* mi_wasm_heap_grow(size_t size, size_t try_alignment) { - uintptr_t base = __builtin_wasm_memory_size(0) * _mi_os_page_size(); - uintptr_t aligned_base = _mi_align_up(base, (uintptr_t) try_alignment); - size_t alloc_size = _mi_align_up( aligned_base - base + size, _mi_os_page_size()); - mi_assert(alloc_size >= size && (alloc_size % _mi_os_page_size()) == 0); - if (alloc_size < size) return NULL; - if (__builtin_wasm_memory_grow(0, alloc_size / _mi_os_page_size()) == SIZE_MAX) { - errno = ENOMEM; - return NULL; - } - return (void*)aligned_base; -} -#else -#define MI_OS_USE_MMAP -static void* mi_unix_mmapx(void* addr, size_t size, size_t try_alignment, int protect_flags, int flags, int fd) { - void* p = NULL; - #if (MI_INTPTR_SIZE >= 8) && !defined(MAP_ALIGNED) - // on 64-bit systems, use the virtual address area after 4TiB for 4MiB aligned allocations - void* hint; - if (addr == NULL && (hint = mi_os_get_aligned_hint(try_alignment, size)) != NULL) { - p = mmap(hint,size,protect_flags,flags,fd,0); - if (p==MAP_FAILED) p = NULL; // fall back to regular mmap - } - #else - UNUSED(try_alignment); - UNUSED(mi_os_get_aligned_hint); - #endif - if (p==NULL) { - p = mmap(addr,size,protect_flags,flags,fd,0); - if (p==MAP_FAILED) p = NULL; - } - return p; -} -static void* mi_unix_mmap(void* addr, size_t size, size_t try_alignment, int protect_flags, bool large_only, bool allow_large, bool* is_large) { - void* p = NULL; - #if !defined(MAP_ANONYMOUS) - #define MAP_ANONYMOUS MAP_ANON - #endif - #if !defined(MAP_NORESERVE) - #define MAP_NORESERVE 0 - #endif - int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; - int fd = -1; - #if defined(MAP_ALIGNED) // BSD - if (try_alignment > 0) { - size_t n = mi_bsr(try_alignment); - if (((size_t)1 << n) == try_alignment && n >= 12 && n <= 30) { // alignment is a power of 2 and 4096 <= alignment <= 1GiB - flags |= MAP_ALIGNED(n); - } - } - #endif - #if defined(PROT_MAX) - protect_flags |= PROT_MAX(PROT_READ | PROT_WRITE); // BSD - #endif - #if defined(VM_MAKE_TAG) - // macOS: tracking anonymous page with a specific ID. (All up to 98 are taken officially but LLVM sanitizers had taken 99) - int os_tag = (int)mi_option_get(mi_option_os_tag); - if (os_tag < 100 || os_tag > 255) os_tag = 100; - fd = VM_MAKE_TAG(os_tag); - #endif - if ((large_only || use_large_os_page(size, try_alignment)) && allow_large) { - static _Atomic(uintptr_t) large_page_try_ok; // = 0; - uintptr_t try_ok = mi_atomic_load_acquire(&large_page_try_ok); - if (!large_only && try_ok > 0) { - // If the OS is not configured for large OS pages, or the user does not have - // enough permission, the `mmap` will always fail (but it might also fail for other reasons). - // Therefore, once a large page allocation failed, we don't try again for `large_page_try_ok` times - // to avoid too many failing calls to mmap. - mi_atomic_cas_strong_acq_rel(&large_page_try_ok, &try_ok, try_ok - 1); - } - else { - int lflags = flags & ~MAP_NORESERVE; // using NORESERVE on huge pages seems to fail on Linux - int lfd = fd; - #ifdef MAP_ALIGNED_SUPER - lflags |= MAP_ALIGNED_SUPER; - #endif - #ifdef MAP_HUGETLB - lflags |= MAP_HUGETLB; - #endif - #ifdef MAP_HUGE_1GB - static bool mi_huge_pages_available = true; - if ((size % GiB) == 0 && mi_huge_pages_available) { - lflags |= MAP_HUGE_1GB; - } - else - #endif - { - #ifdef MAP_HUGE_2MB - lflags |= MAP_HUGE_2MB; - #endif - } - #ifdef VM_FLAGS_SUPERPAGE_SIZE_2MB - lfd |= VM_FLAGS_SUPERPAGE_SIZE_2MB; - #endif - if (large_only || lflags != flags) { - // try large OS page allocation - *is_large = true; - p = mi_unix_mmapx(addr, size, try_alignment, protect_flags, lflags, lfd); - #ifdef MAP_HUGE_1GB - if (p == NULL && (lflags & MAP_HUGE_1GB) != 0) { - mi_huge_pages_available = false; // don't try huge 1GiB pages again - _mi_warning_message("unable to allocate huge (1GiB) page, trying large (2MiB) pages instead (error %i)\n", errno); - lflags = ((lflags & ~MAP_HUGE_1GB) | MAP_HUGE_2MB); - p = mi_unix_mmapx(addr, size, try_alignment, protect_flags, lflags, lfd); - } - #endif - if (large_only) return p; - if (p == NULL) { - mi_atomic_store_release(&large_page_try_ok, (uintptr_t)10); // on error, don't try again for the next N allocations - } - } - } - } - if (p == NULL) { - *is_large = false; - p = mi_unix_mmapx(addr, size, try_alignment, protect_flags, flags, fd); - #if defined(MADV_HUGEPAGE) - // Many Linux systems don't allow MAP_HUGETLB but they support instead - // transparent huge pages (THP). It is not required to call `madvise` with MADV_HUGE - // though since properly aligned allocations will already use large pages if available - // in that case -- in particular for our large regions (in `memory.c`). - // However, some systems only allow THP if called with explicit `madvise`, so - // when large OS pages are enabled for mimalloc, we call `madvise` anyways. - if (allow_large && use_large_os_page(size, try_alignment)) { - if (madvise(p, size, MADV_HUGEPAGE) == 0) { - *is_large = true; // possibly - }; - } - #endif - #if defined(__sun) - if (allow_large && use_large_os_page(size, try_alignment)) { - struct memcntl_mha cmd = {0}; - cmd.mha_pagesize = large_os_page_size; - cmd.mha_cmd = MHA_MAPSIZE_VA; - if (memcntl(p, size, MC_HAT_ADVISE, (caddr_t)&cmd, 0, 0) == 0) { - *is_large = true; - } - } - #endif - } - if (p == NULL) { - _mi_warning_message("unable to allocate OS memory (%zu bytes, error code: %i, address: %p, large only: %d, allow large: %d)\n", size, errno, addr, large_only, allow_large); - } - return p; -} -#endif +/* ----------------------------------------------------------- + aligned hinting +-------------------------------------------------------------- */ // On 64-bit systems, we can do efficient aligned allocation by using -// the 4TiB to 30TiB area to allocate them. -#if (MI_INTPTR_SIZE >= 8) && (defined(_WIN32) || (defined(MI_OS_USE_MMAP) && !defined(MAP_ALIGNED))) -static mi_decl_cache_align _Atomic(uintptr_t) aligned_base; +// the 2TiB to 30TiB area to allocate those. +#if (MI_INTPTR_SIZE >= 8) +static mi_decl_cache_align _Atomic(uintptr_t)aligned_base; -// Return a 4MiB aligned address that is probably available. -// If this returns NULL, the OS will determine the address but on some OS's that may not be +// Return a MI_SEGMENT_SIZE aligned address that is probably available. +// If this returns NULL, the OS will determine the address but on some OS's that may not be // properly aligned which can be more costly as it needs to be adjusted afterwards. -// For a size > 1GiB this always returns NULL in order to guarantee good ASLR randomization; -// (otherwise an initial large allocation of say 2TiB has a 50% chance to include (known) addresses +// For a size > 1GiB this always returns NULL in order to guarantee good ASLR randomization; +// (otherwise an initial large allocation of say 2TiB has a 50% chance to include (known) addresses // in the middle of the 2TiB - 6TiB address range (see issue #372)) -#define KK_HINT_BASE ((uintptr_t)2 << 40) // 2TiB start -#define KK_HINT_AREA ((uintptr_t)4 << 40) // upto 6TiB (since before win8 there is "only" 8TiB available to processes) -#define KK_HINT_MAX ((uintptr_t)30 << 40) // wrap after 30TiB (area after 32TiB is used for huge OS pages) +#define MI_HINT_BASE ((uintptr_t)2 << 40) // 2TiB start +#define MI_HINT_AREA ((uintptr_t)4 << 40) // upto 6TiB (since before win8 there is "only" 8TiB available to processes) +#define MI_HINT_MAX ((uintptr_t)30 << 40) // wrap after 30TiB (area after 32TiB is used for huge OS pages) -static void* mi_os_get_aligned_hint(size_t try_alignment, size_t size) +void* _mi_os_get_aligned_hint(size_t try_alignment, size_t size) { - if (try_alignment == 0 || try_alignment > MI_SEGMENT_SIZE) return NULL; - if ((size%MI_SEGMENT_SIZE) != 0) return NULL; - if (size > 1*GiB) return NULL; // guarantee the chance of fixed valid address is at most 1/(KK_HINT_AREA / 1<<30) = 1/4096. + if (try_alignment <= 1 || try_alignment > MI_SEGMENT_SIZE) return NULL; + size = _mi_align_up(size, MI_SEGMENT_SIZE); + if (size > 1*MI_GiB) return NULL; // guarantee the chance of fixed valid address is at most 1/(MI_HINT_AREA / 1<<30) = 1/4096. #if (MI_SECURE>0) size += MI_SEGMENT_SIZE; // put in `MI_SEGMENT_SIZE` virtual gaps between hinted blocks; this splits VLA's but increases guarded areas. #endif uintptr_t hint = mi_atomic_add_acq_rel(&aligned_base, size); - if (hint == 0 || hint > KK_HINT_MAX) { // wrap or initialize - uintptr_t init = KK_HINT_BASE; + if (hint == 0 || hint > MI_HINT_MAX) { // wrap or initialize + uintptr_t init = MI_HINT_BASE; #if (MI_SECURE>0 || MI_DEBUG==0) // security: randomize start of aligned allocations unless in debug mode - uintptr_t r = _mi_heap_random_next(mi_get_default_heap()); - init = init + ((MI_SEGMENT_SIZE * ((r>>17) & 0xFFFFF)) % KK_HINT_AREA); // (randomly 20 bits)*4MiB == 0 to 4TiB + uintptr_t r = _mi_heap_random_next(mi_prim_get_default_heap()); + init = init + ((MI_SEGMENT_SIZE * ((r>>17) & 0xFFFFF)) % MI_HINT_AREA); // (randomly 20 bits)*4MiB == 0 to 4TiB #endif uintptr_t expected = hint + size; mi_atomic_cas_strong_acq_rel(&aligned_base, &expected, init); - hint = mi_atomic_add_acq_rel(&aligned_base, size); // this may still give 0 or > KK_HINT_MAX but that is ok, it is a hint after all + hint = mi_atomic_add_acq_rel(&aligned_base, size); // this may still give 0 or > MI_HINT_MAX but that is ok, it is a hint after all } if (hint%try_alignment != 0) return NULL; return (void*)hint; } #else -static void* mi_os_get_aligned_hint(size_t try_alignment, size_t size) { - UNUSED(try_alignment); UNUSED(size); +void* _mi_os_get_aligned_hint(size_t try_alignment, size_t size) { + MI_UNUSED(try_alignment); MI_UNUSED(size); return NULL; } #endif -// Primitive allocation from the OS. +/* ----------------------------------------------------------- + Free memory +-------------------------------------------------------------- */ + +static void mi_os_mem_free(void* addr, size_t size, bool was_committed, mi_stats_t* tld_stats) { + MI_UNUSED(tld_stats); + mi_assert_internal((size % _mi_os_page_size()) == 0); + if (addr == NULL || size == 0) return; // || _mi_os_is_huge_reserved(addr) + int err = _mi_prim_free(addr, size); + if (err != 0) { + _mi_warning_message("unable to free OS memory (error: %d (0x%x), size: 0x%zx bytes, address: %p)\n", err, err, size, addr); + } + mi_stats_t* stats = &_mi_stats_main; + if (was_committed) { _mi_stat_decrease(&stats->committed, size); } + _mi_stat_decrease(&stats->reserved, size); +} + + +void _mi_os_free_ex(void* addr, size_t size, bool was_committed, mi_stats_t* tld_stats) { + const size_t csize = _mi_os_good_alloc_size(size); + mi_os_mem_free(addr,csize,was_committed,tld_stats); +} + +void _mi_os_free(void* p, size_t size, mi_stats_t* tld_stats) { + _mi_os_free_ex(p, size, true, tld_stats); +} + + +/* ----------------------------------------------------------- + Primitive allocation from the OS. +-------------------------------------------------------------- */ + // Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned. static void* mi_os_mem_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, mi_stats_t* stats) { mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0); if (size == 0) return NULL; if (!commit) allow_large = false; + if (try_alignment == 0) try_alignment = 1; // avoid 0 to ensure there will be no divide by zero when aligning - void* p = NULL; + void* p = NULL; + int err = _mi_prim_alloc(size, try_alignment, commit, allow_large, is_large, &p); + if (err != 0) { + _mi_warning_message("unable to allocate OS memory (error: %d (0x%x), size: 0x%zx bytes, align: 0x%zx, commit: %d, allow large: %d)\n", err, err, size, try_alignment, commit, allow_large); + } /* if (commit && allow_large) { p = _mi_os_try_alloc_from_huge_reserved(size, try_alignment); @@ -532,17 +181,6 @@ static void* mi_os_mem_alloc(size_t size, size_t try_alignment, bool commit, boo } */ - #if defined(_WIN32) - int flags = MEM_RESERVE; - if (commit) flags |= MEM_COMMIT; - p = mi_win_virtual_alloc(NULL, size, try_alignment, flags, false, allow_large, is_large); - #elif defined(__wasi__) - *is_large = false; - p = mi_wasm_heap_grow(size, try_alignment); - #else - int protect_flags = (commit ? (PROT_WRITE | PROT_READ) : PROT_NONE); - p = mi_unix_mmap(NULL, size, try_alignment, protect_flags, false, allow_large, is_large); - #endif mi_stat_counter_increase(stats->mmap_calls, 1); if (p != NULL) { _mi_stat_increase(&stats->reserved, size); @@ -557,6 +195,7 @@ static void* mi_os_mem_alloc(size_t size, size_t try_alignment, bool commit, boo static void* mi_os_mem_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, bool* is_large, mi_stats_t* stats) { mi_assert_internal(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0)); mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0); + mi_assert_internal(is_large != NULL); if (!commit) allow_large = false; if (!(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0))) return NULL; size = _mi_align_up(size, _mi_os_page_size()); @@ -568,65 +207,53 @@ static void* mi_os_mem_alloc_aligned(size_t size, size_t alignment, bool commit, // if not aligned, free it, overallocate, and unmap around it if (((uintptr_t)p % alignment != 0)) { mi_os_mem_free(p, size, commit, stats); + _mi_warning_message("unable to allocate aligned OS memory directly, fall back to over-allocation (size: 0x%zx bytes, address: %p, alignment: 0x%zx, commit: %d)\n", size, p, alignment, commit); if (size >= (SIZE_MAX - alignment)) return NULL; // overflow - size_t over_size = size + alignment; - -#if _WIN32 - // over-allocate and than re-allocate exactly at an aligned address in there. - // this may fail due to threads allocating at the same time so we - // retry this at most 3 times before giving up. - // (we can not decommit around the overallocation on Windows, because we can only - // free the original pointer, not one pointing inside the area) - int flags = MEM_RESERVE; - if (commit) flags |= MEM_COMMIT; - for (int tries = 0; tries < 3; tries++) { - // over-allocate to determine a virtual memory range - p = mi_os_mem_alloc(over_size, alignment, commit, false, is_large, stats); - if (p == NULL) return NULL; // error - if (((uintptr_t)p % alignment) == 0) { - // if p happens to be aligned, just decommit the left-over area - _mi_os_decommit((uint8_t*)p + size, over_size - size, stats); - break; - } - else { - // otherwise free and allocate at an aligned address in there - mi_os_mem_free(p, over_size, commit, stats); - void* aligned_p = mi_align_up_ptr(p, alignment); - p = mi_win_virtual_alloc(aligned_p, size, alignment, flags, false, allow_large, is_large); - if (p == aligned_p) break; // success! - if (p != NULL) { // should not happen? - mi_os_mem_free(p, size, commit, stats); - p = NULL; - } + const size_t over_size = size + alignment; + + if (mi_os_mem_config.must_free_whole) { // win32 virtualAlloc cannot free parts of an allocate block + // over-allocate uncommitted (virtual) memory + p = mi_os_mem_alloc(over_size, 0 /*alignment*/, false /* commit? */, false /* allow_large */, is_large, stats); + if (p == NULL) return NULL; + + // set p to the aligned part in the full region + // note: this is dangerous on Windows as VirtualFree needs the actual region pointer + // but in mi_os_mem_free we handle this (hopefully exceptional) situation. + p = mi_align_up_ptr(p, alignment); + + // explicitly commit only the aligned part + if (commit) { + _mi_os_commit(p, size, NULL, stats); } } -#else - // overallocate... - p = mi_os_mem_alloc(over_size, alignment, commit, false, is_large, stats); - if (p == NULL) return NULL; - // and selectively unmap parts around the over-allocated area. - void* aligned_p = mi_align_up_ptr(p, alignment); - size_t pre_size = (uint8_t*)aligned_p - (uint8_t*)p; - size_t mid_size = _mi_align_up(size, _mi_os_page_size()); - size_t post_size = over_size - pre_size - mid_size; - mi_assert_internal(pre_size < over_size && post_size < over_size && mid_size >= size); - if (pre_size > 0) mi_os_mem_free(p, pre_size, commit, stats); - if (post_size > 0) mi_os_mem_free((uint8_t*)aligned_p + mid_size, post_size, commit, stats); - // we can return the aligned pointer on `mmap` systems - p = aligned_p; -#endif + else { // mmap can free inside an allocation + // overallocate... + p = mi_os_mem_alloc(over_size, 1, commit, false, is_large, stats); + if (p == NULL) return NULL; + // and selectively unmap parts around the over-allocated area. (noop on sbrk) + void* aligned_p = mi_align_up_ptr(p, alignment); + size_t pre_size = (uint8_t*)aligned_p - (uint8_t*)p; + size_t mid_size = _mi_align_up(size, _mi_os_page_size()); + size_t post_size = over_size - pre_size - mid_size; + mi_assert_internal(pre_size < over_size&& post_size < over_size&& mid_size >= size); + if (pre_size > 0) mi_os_mem_free(p, pre_size, commit, stats); + if (post_size > 0) mi_os_mem_free((uint8_t*)aligned_p + mid_size, post_size, commit, stats); + // we can return the aligned pointer on `mmap` (and sbrk) systems + p = aligned_p; + } } mi_assert_internal(p == NULL || (p != NULL && ((uintptr_t)p % alignment) == 0)); return p; } + /* ----------------------------------------------------------- - OS API: alloc, free, alloc_aligned + OS API: alloc and alloc_aligned ----------------------------------------------------------- */ void* _mi_os_alloc(size_t size, mi_stats_t* tld_stats) { - UNUSED(tld_stats); + MI_UNUSED(tld_stats); mi_stats_t* stats = &_mi_stats_main; if (size == 0) return NULL; size = _mi_os_good_alloc_size(size); @@ -634,21 +261,10 @@ void* _mi_os_alloc(size_t size, mi_stats_t* tld_stats) { return mi_os_mem_alloc(size, 0, true, false, &is_large, stats); } -void _mi_os_free_ex(void* p, size_t size, bool was_committed, mi_stats_t* tld_stats) { - UNUSED(tld_stats); - mi_stats_t* stats = &_mi_stats_main; - if (size == 0 || p == NULL) return; - size = _mi_os_good_alloc_size(size); - mi_os_mem_free(p, size, was_committed, stats); -} - -void _mi_os_free(void* p, size_t size, mi_stats_t* stats) { - _mi_os_free_ex(p, size, true, stats); -} - void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool* large, mi_stats_t* tld_stats) { - UNUSED(tld_stats); + MI_UNUSED(&_mi_os_get_aligned_hint); // suppress unused warnings + MI_UNUSED(tld_stats); if (size == 0) return NULL; size = _mi_os_good_alloc_size(size); alignment = _mi_align_up(alignment, _mi_os_page_size()); @@ -660,13 +276,51 @@ void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool* lar return mi_os_mem_alloc_aligned(size, alignment, commit, allow_large, (large!=NULL?large:&allow_large), &_mi_stats_main /*tld->stats*/ ); } +/* ----------------------------------------------------------- + OS aligned allocation with an offset. This is used + for large alignments > MI_ALIGNMENT_MAX. We use a large mimalloc + page where the object can be aligned at an offset from the start of the segment. + As we may need to overallocate, we need to free such pointers using `mi_free_aligned` + to use the actual start of the memory region. +----------------------------------------------------------- */ + +void* _mi_os_alloc_aligned_offset(size_t size, size_t alignment, size_t offset, bool commit, bool* large, mi_stats_t* tld_stats) { + mi_assert(offset <= MI_SEGMENT_SIZE); + mi_assert(offset <= size); + mi_assert((alignment % _mi_os_page_size()) == 0); + if (offset > MI_SEGMENT_SIZE) return NULL; + if (offset == 0) { + // regular aligned allocation + return _mi_os_alloc_aligned(size, alignment, commit, large, tld_stats); + } + else { + // overallocate to align at an offset + const size_t extra = _mi_align_up(offset, alignment) - offset; + const size_t oversize = size + extra; + void* start = _mi_os_alloc_aligned(oversize, alignment, commit, large, tld_stats); + if (start == NULL) return NULL; + void* p = (uint8_t*)start + extra; + mi_assert(_mi_is_aligned((uint8_t*)p + offset, alignment)); + // decommit the overallocation at the start + if (commit && extra > _mi_os_page_size()) { + _mi_os_decommit(start, extra, tld_stats); + } + return p; + } +} + +void _mi_os_free_aligned(void* p, size_t size, size_t alignment, size_t align_offset, bool was_committed, mi_stats_t* tld_stats) { + mi_assert(align_offset <= MI_SEGMENT_SIZE); + const size_t extra = _mi_align_up(align_offset, alignment) - align_offset; + void* start = (uint8_t*)p - extra; + _mi_os_free_ex(start, size + extra, was_committed, tld_stats); +} /* ----------------------------------------------------------- OS memory API: reset, commit, decommit, protect, unprotect. ----------------------------------------------------------- */ - // OS page align within a given area, either conservative (pages inside the area only), // or not (straddling pages outside the area is possible) static void* mi_os_page_align_areax(bool conservative, void* addr, size_t size, size_t* newsize) { @@ -691,18 +345,6 @@ static void* mi_os_page_align_area_conservative(void* addr, size_t size, size_t* return mi_os_page_align_areax(true, addr, size, newsize); } -static void mi_mprotect_hint(int err) { -#if defined(MI_OS_USE_MMAP) && (MI_SECURE>=2) // guard page around every mimalloc page - if (err == ENOMEM) { - _mi_warning_message("the previous warning may have been caused by a low memory map limit.\n" - " On Linux this is controlled by the vm.max_map_count. For example:\n" - " > sudo sysctl -w vm.max_map_count=262144\n"); - } -#else - UNUSED(err); -#endif -} - // Commit/Decommit memory. // Usually commit is aligned liberal, while decommit is aligned conservative. // (but not for the reset version where we want commit to be conservative as well) @@ -712,7 +354,6 @@ static bool mi_os_commitx(void* addr, size_t size, bool commit, bool conservativ size_t csize; void* start = mi_os_page_align_areax(conservative, addr, size, &csize); if (csize == 0) return true; // || _mi_os_is_huge_reserved(addr)) - int err = 0; if (commit) { _mi_stat_increase(&stats->committed, size); // use size for precise commit vs. decommit _mi_stat_counter_increase(&stats->commit_calls, 1); @@ -721,61 +362,33 @@ static bool mi_os_commitx(void* addr, size_t size, bool commit, bool conservativ _mi_stat_decrease(&stats->committed, size); } - #if defined(_WIN32) - if (commit) { - // if the memory was already committed, the call succeeds but it is not zero'd - // *is_zero = true; - void* p = VirtualAlloc(start, csize, MEM_COMMIT, PAGE_READWRITE); - err = (p == start ? 0 : GetLastError()); - } - else { - BOOL ok = VirtualFree(start, csize, MEM_DECOMMIT); - err = (ok ? 0 : GetLastError()); - } - #elif defined(__wasi__) - // WebAssembly guests can't control memory protection - #elif defined(MAP_FIXED) - if (!commit) { - // use mmap with MAP_FIXED to discard the existing memory (and reduce commit charge) - void* p = mmap(start, csize, PROT_NONE, (MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE), -1, 0); - if (p != start) { err = errno; } - } - else { - // for commit, just change the protection - err = mprotect(start, csize, (PROT_READ | PROT_WRITE)); - if (err != 0) { err = errno; } - #if defined(MADV_FREE_REUSE) - while ((err = madvise(start, csize, MADV_FREE_REUSE)) != 0 && errno == EAGAIN) { errno = 0; } - #endif - } - #else - err = mprotect(start, csize, (commit ? (PROT_READ | PROT_WRITE) : PROT_NONE)); - if (err != 0) { err = errno; } - #endif + int err = _mi_prim_commit(start, csize, commit); if (err != 0) { - _mi_warning_message("%s error: start: %p, csize: 0x%x, err: %i\n", commit ? "commit" : "decommit", start, csize, err); - mi_mprotect_hint(err); + _mi_warning_message("cannot %s OS memory (error: %d (0x%x), address: %p, size: 0x%zx bytes)\n", commit ? "commit" : "decommit", err, err, start, csize); } mi_assert_internal(err == 0); return (err == 0); } bool _mi_os_commit(void* addr, size_t size, bool* is_zero, mi_stats_t* tld_stats) { - UNUSED(tld_stats); + MI_UNUSED(tld_stats); mi_stats_t* stats = &_mi_stats_main; return mi_os_commitx(addr, size, true, false /* liberal */, is_zero, stats); } bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* tld_stats) { - UNUSED(tld_stats); + MI_UNUSED(tld_stats); mi_stats_t* stats = &_mi_stats_main; bool is_zero; return mi_os_commitx(addr, size, false, true /* conservative */, &is_zero, stats); } +/* static bool mi_os_commit_unreset(void* addr, size_t size, bool* is_zero, mi_stats_t* stats) { - return mi_os_commitx(addr, size, true, true /* conservative */, is_zero, stats); + return mi_os_commitx(addr, size, true, true // conservative + , is_zero, stats); } +*/ // Signal to the OS that the address range is no longer in use // but may be used later again. This will release physical memory @@ -790,50 +403,15 @@ static bool mi_os_resetx(void* addr, size_t size, bool reset, mi_stats_t* stats) else _mi_stat_decrease(&stats->reset, csize); if (!reset) return true; // nothing to do on unreset! - #if (MI_DEBUG>1) - if (MI_SECURE==0) { - memset(start, 0, csize); // pretend it is eagerly reset - } + #if (MI_DEBUG>1) && !MI_SECURE && !MI_TRACK_ENABLED // && !MI_TSAN + memset(start, 0, csize); // pretend it is eagerly reset #endif -#if defined(_WIN32) - // Testing shows that for us (on `malloc-large`) MEM_RESET is 2x faster than DiscardVirtualMemory - void* p = VirtualAlloc(start, csize, MEM_RESET, PAGE_READWRITE); - mi_assert_internal(p == start); - #if 1 - if (p == start && start != NULL) { - VirtualUnlock(start,csize); // VirtualUnlock after MEM_RESET removes the memory from the working set - } - #endif - if (p != start) return false; -#else -#if defined(MADV_FREE) - #if defined(MADV_FREE_REUSABLE) - #define KK_MADV_FREE_INITIAL MADV_FREE_REUSABLE - #else - #define KK_MADV_FREE_INITIAL MADV_FREE - #endif - static _Atomic(uintptr_t) advice = ATOMIC_VAR_INIT(KK_MADV_FREE_INITIAL); - int oadvice = (int)mi_atomic_load_relaxed(&advice); - int err; - while ((err = madvise(start, csize, oadvice)) != 0 && errno == EAGAIN) { errno = 0; }; - if (err != 0 && errno == EINVAL && oadvice == KK_MADV_FREE_INITIAL) { - // if MADV_FREE/MADV_FREE_REUSABLE is not supported, fall back to MADV_DONTNEED from now on - mi_atomic_store_release(&advice, (uintptr_t)MADV_DONTNEED); - err = madvise(start, csize, MADV_DONTNEED); - } -#elif defined(__wasi__) - int err = 0; -#else - int err = madvise(start, csize, MADV_DONTNEED); -#endif + int err = _mi_prim_reset(start, csize); if (err != 0) { - _mi_warning_message("madvise reset error: start: %p, csize: 0x%x, errno: %i\n", start, csize, errno); + _mi_warning_message("cannot reset OS memory (error: %d (0x%x), address: %p, size: 0x%zx bytes)\n", err, err, start, csize); } - //mi_assert(err == 0); - if (err != 0) return false; -#endif - return true; + return (err == 0); } // Signal to the OS that the address range is no longer in use @@ -841,28 +419,19 @@ static bool mi_os_resetx(void* addr, size_t size, bool reset, mi_stats_t* stats) // pages and reduce swapping while keeping the memory committed. // We page align to a conservative area inside the range to reset. bool _mi_os_reset(void* addr, size_t size, mi_stats_t* tld_stats) { - UNUSED(tld_stats); + MI_UNUSED(tld_stats); mi_stats_t* stats = &_mi_stats_main; - if (mi_option_is_enabled(mi_option_reset_decommits)) { - return _mi_os_decommit(addr, size, stats); - } - else { - return mi_os_resetx(addr, size, true, stats); - } + return mi_os_resetx(addr, size, true, stats); } +/* bool _mi_os_unreset(void* addr, size_t size, bool* is_zero, mi_stats_t* tld_stats) { - UNUSED(tld_stats); + MI_UNUSED(tld_stats); mi_stats_t* stats = &_mi_stats_main; - if (mi_option_is_enabled(mi_option_reset_decommits)) { - return mi_os_commit_unreset(addr, size, is_zero, stats); // re-commit it (conservatively!) - } - else { - *is_zero = false; - return mi_os_resetx(addr, size, false, stats); - } + *is_zero = false; + return mi_os_resetx(addr, size, false, stats); } - +*/ // Protect a region in memory to be not accessible. static bool mi_os_protectx(void* addr, size_t size, bool protect) { @@ -875,20 +444,9 @@ static bool mi_os_protectx(void* addr, size_t size, bool protect) { _mi_warning_message("cannot mprotect memory allocated in huge OS pages\n"); } */ - int err = 0; -#ifdef _WIN32 - DWORD oldprotect = 0; - BOOL ok = VirtualProtect(start, csize, protect ? PAGE_NOACCESS : PAGE_READWRITE, &oldprotect); - err = (ok ? 0 : GetLastError()); -#elif defined(__wasi__) - err = 0; -#else - err = mprotect(start, csize, protect ? PROT_NONE : (PROT_READ | PROT_WRITE)); - if (err != 0) { err = errno; } -#endif + int err = _mi_prim_protect(start,csize,protect); if (err != 0) { - _mi_warning_message("mprotect error: start: %p, csize: 0x%x, err: %i\n", start, csize, err); - mi_mprotect_hint(err); + _mi_warning_message("cannot %s OS memory (error: %d (0x%x), address: %p, size: 0x%zx bytes)\n", (protect ? "protect" : "unprotect"), err, err, start, csize); } return (err == 0); } @@ -903,121 +461,12 @@ bool _mi_os_unprotect(void* addr, size_t size) { -bool _mi_os_shrink(void* p, size_t oldsize, size_t newsize, mi_stats_t* stats) { - // page align conservatively within the range - mi_assert_internal(oldsize > newsize && p != NULL); - if (oldsize < newsize || p == NULL) return false; - if (oldsize == newsize) return true; - - // oldsize and newsize should be page aligned or we cannot shrink precisely - void* addr = (uint8_t*)p + newsize; - size_t size = 0; - void* start = mi_os_page_align_area_conservative(addr, oldsize - newsize, &size); - if (size == 0 || start != addr) return false; - -#ifdef _WIN32 - // we cannot shrink on windows, but we can decommit - return _mi_os_decommit(start, size, stats); -#else - return mi_os_mem_free(start, size, true, stats); -#endif -} - - /* ---------------------------------------------------------------------------- Support for allocating huge OS pages (1Gib) that are reserved up-front and possibly associated with a specific NUMA node. (use `numa_node>=0`) -----------------------------------------------------------------------------*/ -#define MI_HUGE_OS_PAGE_SIZE (GiB) +#define MI_HUGE_OS_PAGE_SIZE (MI_GiB) -#if defined(_WIN32) && (MI_INTPTR_SIZE >= 8) -static void* mi_os_alloc_huge_os_pagesx(void* addr, size_t size, int numa_node) -{ - mi_assert_internal(size%GiB == 0); - mi_assert_internal(addr != NULL); - const DWORD flags = MEM_LARGE_PAGES | MEM_COMMIT | MEM_RESERVE; - - mi_win_enable_large_os_pages(); - - #if defined(MEM_EXTENDED_PARAMETER_TYPE_BITS) - MEM_EXTENDED_PARAMETER params[3] = { {{0,0},{0}},{{0,0},{0}},{{0,0},{0}} }; - // on modern Windows try use NtAllocateVirtualMemoryEx for 1GiB huge pages - static bool mi_huge_pages_available = true; - if (pNtAllocateVirtualMemoryEx != NULL && mi_huge_pages_available) { - #ifndef MEM_EXTENDED_PARAMETER_NONPAGED_HUGE - #define MEM_EXTENDED_PARAMETER_NONPAGED_HUGE (0x10) - #endif - params[0].Type = 5; // == MemExtendedParameterAttributeFlags; - params[0].ULong64 = MEM_EXTENDED_PARAMETER_NONPAGED_HUGE; - ULONG param_count = 1; - if (numa_node >= 0) { - param_count++; - params[1].Type = MemExtendedParameterNumaNode; - params[1].ULong = (unsigned)numa_node; - } - SIZE_T psize = size; - void* base = addr; - NTSTATUS err = (*pNtAllocateVirtualMemoryEx)(GetCurrentProcess(), &base, &psize, flags, PAGE_READWRITE, params, param_count); - if (err == 0 && base != NULL) { - return base; - } - else { - // fall back to regular large pages - mi_huge_pages_available = false; // don't try further huge pages - _mi_warning_message("unable to allocate using huge (1gb) pages, trying large (2mb) pages instead (status 0x%lx)\n", err); - } - } - // on modern Windows try use VirtualAlloc2 for numa aware large OS page allocation - if (pVirtualAlloc2 != NULL && numa_node >= 0) { - params[0].Type = MemExtendedParameterNumaNode; - params[0].ULong = (unsigned)numa_node; - return (*pVirtualAlloc2)(GetCurrentProcess(), addr, size, flags, PAGE_READWRITE, params, 1); - } - #else - UNUSED(numa_node); - #endif - // otherwise use regular virtual alloc on older windows - return VirtualAlloc(addr, size, flags, PAGE_READWRITE); -} - -#elif defined(MI_OS_USE_MMAP) && (MI_INTPTR_SIZE >= 8) && !defined(__HAIKU__) -#include -#ifndef MPOL_PREFERRED -#define MPOL_PREFERRED 1 -#endif -#if defined(SYS_mbind) -static long mi_os_mbind(void* start, unsigned long len, unsigned long mode, const unsigned long* nmask, unsigned long maxnode, unsigned flags) { - return syscall(SYS_mbind, start, len, mode, nmask, maxnode, flags); -} -#else -static long mi_os_mbind(void* start, unsigned long len, unsigned long mode, const unsigned long* nmask, unsigned long maxnode, unsigned flags) { - UNUSED(start); UNUSED(len); UNUSED(mode); UNUSED(nmask); UNUSED(maxnode); UNUSED(flags); - return 0; -} -#endif -static void* mi_os_alloc_huge_os_pagesx(void* addr, size_t size, int numa_node) { - mi_assert_internal(size%GiB == 0); - bool is_large = true; - void* p = mi_unix_mmap(addr, size, MI_SEGMENT_SIZE, PROT_READ | PROT_WRITE, true, true, &is_large); - if (p == NULL) return NULL; - if (numa_node >= 0 && numa_node < 8*MI_INTPTR_SIZE) { // at most 64 nodes - uintptr_t numa_mask = (1UL << numa_node); - // TODO: does `mbind` work correctly for huge OS pages? should we - // use `set_mempolicy` before calling mmap instead? - // see: - long err = mi_os_mbind(p, size, MPOL_PREFERRED, &numa_mask, 8*MI_INTPTR_SIZE, 0); - if (err != 0) { - _mi_warning_message("failed to bind huge (1gb) pages to numa node %d: %s\n", numa_node, strerror(errno)); - } - } - return p; -} -#else -static void* mi_os_alloc_huge_os_pagesx(void* addr, size_t size, int numa_node) { - UNUSED(addr); UNUSED(size); UNUSED(numa_node); - return NULL; -} -#endif #if (MI_INTPTR_SIZE >= 8) // To ensure proper alignment, use our own area for huge OS pages @@ -1036,10 +485,10 @@ static uint8_t* mi_os_claim_huge_pages(size_t pages, size_t* total_size) { if (start == 0) { // Initialize the start address after the 32TiB area start = ((uintptr_t)32 << 40); // 32TiB virtual start address -#if (MI_SECURE>0 || MI_DEBUG==0) // security: randomize start of huge pages unless in debug mode - uintptr_t r = _mi_heap_random_next(mi_get_default_heap()); + #if (MI_SECURE>0 || MI_DEBUG==0) // security: randomize start of huge pages unless in debug mode + uintptr_t r = _mi_heap_random_next(mi_prim_get_default_heap()); start = start + ((uintptr_t)MI_HUGE_OS_PAGE_SIZE * ((r>>17) & 0x0FFF)); // (randomly 12bits)*1GiB == between 0 to 4TiB -#endif + #endif } end = start + size; mi_assert_internal(end % MI_SEGMENT_SIZE == 0); @@ -1050,7 +499,7 @@ static uint8_t* mi_os_claim_huge_pages(size_t pages, size_t* total_size) { } #else static uint8_t* mi_os_claim_huge_pages(size_t pages, size_t* total_size) { - UNUSED(pages); + MI_UNUSED(pages); if (total_size != NULL) *total_size = 0; return NULL; } @@ -1068,23 +517,29 @@ void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_mse // We allocate one page at the time to be able to abort if it takes too long // or to at least allocate as many as available on the system. mi_msecs_t start_t = _mi_clock_start(); - size_t page; - for (page = 0; page < pages; page++) { + size_t page = 0; + while (page < pages) { // allocate a page void* addr = start + (page * MI_HUGE_OS_PAGE_SIZE); - void* p = mi_os_alloc_huge_os_pagesx(addr, MI_HUGE_OS_PAGE_SIZE, numa_node); + void* p = NULL; + int err = _mi_prim_alloc_huge_os_pages(addr, MI_HUGE_OS_PAGE_SIZE, numa_node, &p); + if (err != 0) { + _mi_warning_message("unable to allocate huge OS page (error: %d (0x%x), address: %p, size: %zx bytes)\n", err, err, addr, MI_HUGE_OS_PAGE_SIZE); + break; + } // Did we succeed at a contiguous address? if (p != addr) { // no success, issue a warning and break if (p != NULL) { - _mi_warning_message("could not allocate contiguous huge page %zu at %p\n", page, addr); + _mi_warning_message("could not allocate contiguous huge OS page %zu at %p\n", page, addr); _mi_os_free(p, MI_HUGE_OS_PAGE_SIZE, &_mi_stats_main); } break; } // success, record it + page++; // increase before timeout check (see issue #711) _mi_stat_increase(&_mi_stats_main.committed, MI_HUGE_OS_PAGE_SIZE); _mi_stat_increase(&_mi_stats_main.reserved, MI_HUGE_OS_PAGE_SIZE); @@ -1098,14 +553,14 @@ void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_mse } } if (elapsed > max_msecs) { - _mi_warning_message("huge page allocation timed out\n"); + _mi_warning_message("huge OS page allocation timed out (after allocating %zu page(s))\n", page); break; } } } mi_assert_internal(page*MI_HUGE_OS_PAGE_SIZE <= size); - if (pages_reserved != NULL) *pages_reserved = page; - if (psize != NULL) *psize = page * MI_HUGE_OS_PAGE_SIZE; + if (pages_reserved != NULL) { *pages_reserved = page; } + if (psize != NULL) { *psize = page * MI_HUGE_OS_PAGE_SIZE; } return (page == 0 ? NULL : start); } @@ -1117,90 +572,13 @@ void _mi_os_free_huge_pages(void* p, size_t size, mi_stats_t* stats) { while (size >= MI_HUGE_OS_PAGE_SIZE) { _mi_os_free(base, MI_HUGE_OS_PAGE_SIZE, stats); size -= MI_HUGE_OS_PAGE_SIZE; + base += MI_HUGE_OS_PAGE_SIZE; } } /* ---------------------------------------------------------------------------- Support NUMA aware allocation -----------------------------------------------------------------------------*/ -#ifdef _WIN32 -static size_t mi_os_numa_nodex() { - USHORT numa_node = 0; - if (pGetCurrentProcessorNumberEx != NULL && pGetNumaProcessorNodeEx != NULL) { - // Extended API is supported - PROCESSOR_NUMBER pnum; - (*pGetCurrentProcessorNumberEx)(&pnum); - USHORT nnode = 0; - BOOL ok = (*pGetNumaProcessorNodeEx)(&pnum, &nnode); - if (ok) numa_node = nnode; - } - else { - // Vista or earlier, use older API that is limited to 64 processors. Issue #277 - DWORD pnum = GetCurrentProcessorNumber(); - UCHAR nnode = 0; - BOOL ok = GetNumaProcessorNode((UCHAR)pnum, &nnode); - if (ok) numa_node = nnode; - } - return numa_node; -} - -static size_t mi_os_numa_node_countx(void) { - ULONG numa_max = 0; - GetNumaHighestNodeNumber(&numa_max); - // find the highest node number that has actual processors assigned to it. Issue #282 - while(numa_max > 0) { - if (pGetNumaNodeProcessorMaskEx != NULL) { - // Extended API is supported - GROUP_AFFINITY affinity; - if ((*pGetNumaNodeProcessorMaskEx)((USHORT)numa_max, &affinity)) { - if (affinity.Mask != 0) break; // found the maximum non-empty node - } - } - else { - // Vista or earlier, use older API that is limited to 64 processors. - ULONGLONG mask; - if (GetNumaNodeProcessorMask((UCHAR)numa_max, &mask)) { - if (mask != 0) break; // found the maximum non-empty node - }; - } - // max node was invalid or had no processor assigned, try again - numa_max--; - } - return ((size_t)numa_max + 1); -} -#elif defined(__linux__) -#include // getcpu -#include // access - -static size_t mi_os_numa_nodex(void) { -#ifdef SYS_getcpu - unsigned long node = 0; - unsigned long ncpu = 0; - long err = syscall(SYS_getcpu, &ncpu, &node, NULL); - if (err != 0) return 0; - return node; -#else - return 0; -#endif -} -static size_t mi_os_numa_node_countx(void) { - char buf[128]; - unsigned node = 0; - for(node = 0; node < 256; node++) { - // enumerate node entries -- todo: it there a more efficient way to do this? (but ensure there is no allocation) - snprintf(buf, 127, "/sys/devices/system/node/node%u", node + 1); - if (access(buf,R_OK) != 0) break; - } - return (node+1); -} -#else -static size_t mi_os_numa_nodex(void) { - return 0; -} -static size_t mi_os_numa_node_countx(void) { - return 1; -} -#endif _Atomic(size_t) _mi_numa_node_count; // = 0 // cache the node count @@ -1212,9 +590,9 @@ size_t _mi_os_numa_node_count_get(void) { count = (size_t)ncount; } else { - count = mi_os_numa_node_countx(); // or detect dynamically + count = _mi_prim_numa_node_count(); // or detect dynamically if (count == 0) count = 1; - } + } mi_atomic_store_release(&_mi_numa_node_count, count); // save it _mi_verbose_message("using %zd numa regions\n", count); } @@ -1222,11 +600,11 @@ size_t _mi_os_numa_node_count_get(void) { } int _mi_os_numa_node_get(mi_os_tld_t* tld) { - UNUSED(tld); + MI_UNUSED(tld); size_t numa_count = _mi_os_numa_node_count(); if (numa_count<=1) return 0; // optimize on single numa node systems: always node 0 // never more than the node count and >= 0 - size_t numa_node = mi_os_numa_nodex(); + size_t numa_node = _mi_prim_numa_node(); if (numa_node >= numa_count) { numa_node = numa_node % numa_count; } return (int)numa_node; } diff --git a/Source/mimalloc/src/page-queue.c b/Source/mimalloc/src/page-queue.c index 365257e76..cb54b3740 100644 --- a/Source/mimalloc/src/page-queue.c +++ b/Source/mimalloc/src/page-queue.c @@ -34,15 +34,15 @@ terms of the MIT license. A copy of the license can be found in the file static inline bool mi_page_queue_is_huge(const mi_page_queue_t* pq) { - return (pq->block_size == (MI_LARGE_OBJ_SIZE_MAX+sizeof(uintptr_t))); + return (pq->block_size == (MI_MEDIUM_OBJ_SIZE_MAX+sizeof(uintptr_t))); } static inline bool mi_page_queue_is_full(const mi_page_queue_t* pq) { - return (pq->block_size == (MI_LARGE_OBJ_SIZE_MAX+(2*sizeof(uintptr_t)))); + return (pq->block_size == (MI_MEDIUM_OBJ_SIZE_MAX+(2*sizeof(uintptr_t)))); } static inline bool mi_page_queue_is_special(const mi_page_queue_t* pq) { - return (pq->block_size > MI_LARGE_OBJ_SIZE_MAX); + return (pq->block_size > MI_MEDIUM_OBJ_SIZE_MAX); } /* ----------------------------------------------------------- @@ -53,7 +53,7 @@ static inline bool mi_page_queue_is_special(const mi_page_queue_t* pq) { // Returns MI_BIN_HUGE if the size is too large. // We use `wsize` for the size in "machine word sizes", // i.e. byte size == `wsize*sizeof(void*)`. -extern inline uint8_t _mi_bin(size_t size) { +static inline uint8_t mi_bin(size_t size) { size_t wsize = _mi_wsize_from_size(size); uint8_t bin; if (wsize <= 1) { @@ -72,11 +72,11 @@ extern inline uint8_t _mi_bin(size_t size) { bin = (uint8_t)wsize; } #endif - else if (wsize > MI_LARGE_OBJ_WSIZE_MAX) { + else if (wsize > MI_MEDIUM_OBJ_WSIZE_MAX) { bin = MI_BIN_HUGE; } else { - #if defined(MI_ALIGN4W) + #if defined(MI_ALIGN4W) if (wsize <= 16) { wsize = (wsize+3)&~3; } // round to 4x word sizes #endif wsize--; @@ -98,14 +98,18 @@ extern inline uint8_t _mi_bin(size_t size) { Queue of pages with free blocks ----------------------------------------------------------- */ +uint8_t _mi_bin(size_t size) { + return mi_bin(size); +} + size_t _mi_bin_size(uint8_t bin) { return _mi_heap_empty.pages[bin].block_size; } // Good size for allocation size_t mi_good_size(size_t size) mi_attr_noexcept { - if (size <= MI_LARGE_OBJ_SIZE_MAX) { - return _mi_bin_size(_mi_bin(size)); + if (size <= MI_MEDIUM_OBJ_SIZE_MAX) { + return _mi_bin_size(mi_bin(size)); } else { return _mi_align_up(size,_mi_os_page_size()); @@ -134,7 +138,7 @@ static bool mi_heap_contains_queue(const mi_heap_t* heap, const mi_page_queue_t* #endif static mi_page_queue_t* mi_page_queue_of(const mi_page_t* page) { - uint8_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : _mi_bin(page->xblock_size)); + uint8_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : mi_bin(page->xblock_size)); mi_heap_t* heap = mi_page_heap(page); mi_assert_internal(heap != NULL && bin <= MI_BIN_FULL); mi_page_queue_t* pq = &heap->pages[bin]; @@ -144,7 +148,7 @@ static mi_page_queue_t* mi_page_queue_of(const mi_page_t* page) { } static mi_page_queue_t* mi_heap_page_queue_of(mi_heap_t* heap, const mi_page_t* page) { - uint8_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : _mi_bin(page->xblock_size)); + uint8_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : mi_bin(page->xblock_size)); mi_assert_internal(bin <= MI_BIN_FULL); mi_page_queue_t* pq = &heap->pages[bin]; mi_assert_internal(mi_page_is_in_full(page) || page->xblock_size == pq->block_size); @@ -177,9 +181,9 @@ static inline void mi_heap_queue_first_update(mi_heap_t* heap, const mi_page_que } else { // find previous size; due to minimal alignment upto 3 previous bins may need to be skipped - uint8_t bin = _mi_bin(size); + uint8_t bin = mi_bin(size); const mi_page_queue_t* prev = pq - 1; - while( bin == _mi_bin(prev->block_size) && prev > &heap->pages[0]) { + while( bin == mi_bin(prev->block_size) && prev > &heap->pages[0]) { prev--; } start = 1 + _mi_wsize_from_size(prev->block_size); @@ -202,8 +206,9 @@ static bool mi_page_queue_is_empty(mi_page_queue_t* queue) { static void mi_page_queue_remove(mi_page_queue_t* queue, mi_page_t* page) { mi_assert_internal(page != NULL); mi_assert_expensive(mi_page_queue_contains(queue, page)); - mi_assert_internal(page->xblock_size == queue->block_size || (page->xblock_size > MI_LARGE_OBJ_SIZE_MAX && mi_page_queue_is_huge(queue)) || (mi_page_is_in_full(page) && mi_page_queue_is_full(queue))); + mi_assert_internal(page->xblock_size == queue->block_size || (page->xblock_size > MI_MEDIUM_OBJ_SIZE_MAX && mi_page_queue_is_huge(queue)) || (mi_page_is_in_full(page) && mi_page_queue_is_full(queue))); mi_heap_t* heap = mi_page_heap(page); + if (page->prev != NULL) page->prev->next = page->next; if (page->next != NULL) page->next->prev = page->prev; if (page == queue->last) queue->last = page->prev; @@ -224,9 +229,11 @@ static void mi_page_queue_remove(mi_page_queue_t* queue, mi_page_t* page) { static void mi_page_queue_push(mi_heap_t* heap, mi_page_queue_t* queue, mi_page_t* page) { mi_assert_internal(mi_page_heap(page) == heap); mi_assert_internal(!mi_page_queue_contains(queue, page)); - mi_assert_internal(_mi_page_segment(page)->page_kind != MI_PAGE_HUGE); + #if MI_HUGE_PAGE_ABANDON + mi_assert_internal(_mi_page_segment(page)->kind != MI_SEGMENT_HUGE); + #endif mi_assert_internal(page->xblock_size == queue->block_size || - (page->xblock_size > MI_LARGE_OBJ_SIZE_MAX && mi_page_queue_is_huge(queue)) || + (page->xblock_size > MI_MEDIUM_OBJ_SIZE_MAX) || (mi_page_is_in_full(page) && mi_page_queue_is_full(queue))); mi_page_set_in_full(page, mi_page_queue_is_full(queue)); @@ -252,6 +259,7 @@ static void mi_page_queue_enqueue_from(mi_page_queue_t* to, mi_page_queue_t* fro mi_assert_internal(page != NULL); mi_assert_expensive(mi_page_queue_contains(from, page)); mi_assert_expensive(!mi_page_queue_contains(to, page)); + mi_assert_internal((page->xblock_size == to->block_size && page->xblock_size == from->block_size) || (page->xblock_size == to->block_size && mi_page_queue_is_full(from)) || (page->xblock_size == from->block_size && mi_page_queue_is_full(to)) || @@ -297,7 +305,7 @@ size_t _mi_page_queue_append(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_queue for (mi_page_t* page = append->first; page != NULL; page = page->next) { // inline `mi_page_set_heap` to avoid wrong assertion during absorption; // in this case it is ok to be delayed freeing since both "to" and "from" heap are still alive. - mi_atomic_store_release(&page->xheap, (uintptr_t)heap); + mi_atomic_store_release(&page->xheap, (uintptr_t)heap); // set the flag to delayed free (not overriding NEVER_DELAYED_FREE) which has as a // side effect that it spins until any DELAYED_FREEING is finished. This ensures // that after appending only the new heap will be used for delayed free operations. diff --git a/Source/mimalloc/src/page.c b/Source/mimalloc/src/page.c index c08be9c00..cae6b5813 100644 --- a/Source/mimalloc/src/page.c +++ b/Source/mimalloc/src/page.c @@ -7,13 +7,13 @@ terms of the MIT license. A copy of the license can be found in the file /* ----------------------------------------------------------- The core of the allocator. Every segment contains - pages of a {certain block size. The main function + pages of a certain block size. The main function exported is `mi_malloc_generic`. ----------------------------------------------------------- */ #include "mimalloc.h" -#include "mimalloc-internal.h" -#include "mimalloc-atomic.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" /* ----------------------------------------------------------- Definition of page queues for each block size @@ -30,7 +30,7 @@ terms of the MIT license. A copy of the license can be found in the file // Index a block in a page static inline mi_block_t* mi_page_block_at(const mi_page_t* page, void* page_start, size_t block_size, size_t i) { - UNUSED(page); + MI_UNUSED(page); mi_assert_internal(page != NULL); mi_assert_internal(i <= page->reserved); return (mi_block_t*)((uint8_t*)page_start + (i * block_size)); @@ -74,27 +74,30 @@ static bool mi_page_is_valid_init(mi_page_t* page) { mi_assert_internal(page->used <= page->capacity); mi_assert_internal(page->capacity <= page->reserved); - const size_t bsize = mi_page_block_size(page); mi_segment_t* segment = _mi_page_segment(page); uint8_t* start = _mi_page_start(segment,page,NULL); - mi_assert_internal(start == _mi_segment_page_start(segment,page,bsize,NULL,NULL)); + mi_assert_internal(start == _mi_segment_page_start(segment,page,NULL)); + //const size_t bsize = mi_page_block_size(page); //mi_assert_internal(start + page->capacity*page->block_size == page->top); mi_assert_internal(mi_page_list_is_valid(page,page->free)); mi_assert_internal(mi_page_list_is_valid(page,page->local_free)); #if MI_DEBUG>3 // generally too expensive to check this - if (page->flags.is_zero) { - for(mi_block_t* block = page->free; block != NULL; mi_block_next(page,block)) { - mi_assert_expensive(mi_mem_is_zero(block + 1, page->block_size - sizeof(mi_block_t))); + if (page->is_zero) { + const size_t ubsize = mi_page_usable_block_size(page); + for(mi_block_t* block = page->free; block != NULL; block = mi_block_next(page,block)) { + mi_assert_expensive(mi_mem_is_zero(block + 1, ubsize - sizeof(mi_block_t))); } } #endif + #if !MI_TRACK_ENABLED && !MI_TSAN mi_block_t* tfree = mi_page_thread_free(page); mi_assert_internal(mi_page_list_is_valid(page, tfree)); //size_t tfree_count = mi_page_list_count(page, tfree); //mi_assert_internal(tfree_count <= page->thread_freed + 1); + #endif size_t free_count = mi_page_list_count(page, page->free) + mi_page_list_count(page, page->local_free); mi_assert_internal(page->used + free_count == page->capacity); @@ -102,6 +105,8 @@ static bool mi_page_is_valid_init(mi_page_t* page) { return true; } +extern bool _mi_process_is_initialized; // has mi_process_init been called? + bool _mi_page_is_valid(mi_page_t* page) { mi_assert_internal(mi_page_is_valid_init(page)); #if MI_SECURE @@ -109,11 +114,15 @@ bool _mi_page_is_valid(mi_page_t* page) { #endif if (mi_page_heap(page)!=NULL) { mi_segment_t* segment = _mi_page_segment(page); - mi_assert_internal(!_mi_process_is_initialized || segment->thread_id == mi_page_heap(page)->thread_id || segment->thread_id==0); - if (segment->page_kind != MI_PAGE_HUGE) { + + mi_assert_internal(!_mi_process_is_initialized || segment->thread_id==0 || segment->thread_id == mi_page_heap(page)->thread_id); + #if MI_HUGE_PAGE_ABANDON + if (segment->kind != MI_SEGMENT_HUGE) + #endif + { mi_page_queue_t* pq = mi_page_queue_of(page); mi_assert_internal(mi_page_queue_contains(pq, page)); - mi_assert_internal(pq->block_size==mi_page_block_size(page) || mi_page_block_size(page) > MI_LARGE_OBJ_SIZE_MAX || mi_page_is_in_full(page)); + mi_assert_internal(pq->block_size==mi_page_block_size(page) || mi_page_block_size(page) > MI_MEDIUM_OBJ_SIZE_MAX || mi_page_is_in_full(page)); mi_assert_internal(mi_heap_contains_queue(mi_page_heap(page),pq)); } } @@ -122,14 +131,23 @@ bool _mi_page_is_valid(mi_page_t* page) { #endif void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never) { + while (!_mi_page_try_use_delayed_free(page, delay, override_never)) { + mi_atomic_yield(); + } +} + +bool _mi_page_try_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never) { mi_thread_free_t tfreex; mi_delayed_t old_delay; - mi_thread_free_t tfree; + mi_thread_free_t tfree; + size_t yield_count = 0; do { tfree = mi_atomic_load_acquire(&page->xthread_free); // note: must acquire as we can break/repeat this loop and not do a CAS; tfreex = mi_tf_set_delayed(tfree, delay); old_delay = mi_tf_delayed(tfree); - if (mi_unlikely(old_delay == MI_DELAYED_FREEING)) { + if mi_unlikely(old_delay == MI_DELAYED_FREEING) { + if (yield_count >= 4) return false; // give up after 4 tries + yield_count++; mi_atomic_yield(); // delay until outstanding MI_DELAYED_FREEING are done. // tfree = mi_tf_set_delayed(tfree, MI_NO_DELAYED_FREE); // will cause CAS to busy fail } @@ -141,6 +159,8 @@ void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool overrid } } while ((old_delay == MI_DELAYED_FREEING) || !mi_atomic_cas_weak_release(&page->xthread_free, &tfree, tfreex)); + + return true; // success } /* ----------------------------------------------------------- @@ -197,7 +217,7 @@ void _mi_page_free_collect(mi_page_t* page, bool force) { // and the local free list if (page->local_free != NULL) { - if (mi_likely(page->free == NULL)) { + if mi_likely(page->free == NULL) { // usual case page->free = page->local_free; page->local_free = NULL; @@ -229,9 +249,12 @@ void _mi_page_free_collect(mi_page_t* page, bool force) { // called from segments when reclaiming abandoned pages void _mi_page_reclaim(mi_heap_t* heap, mi_page_t* page) { mi_assert_expensive(mi_page_is_valid_init(page)); + mi_assert_internal(mi_page_heap(page) == heap); mi_assert_internal(mi_page_thread_free_flag(page) != MI_NEVER_DELAYED_FREE); - mi_assert_internal(_mi_page_segment(page)->page_kind != MI_PAGE_HUGE); + #if MI_HUGE_PAGE_ABANDON + mi_assert_internal(_mi_page_segment(page)->kind != MI_SEGMENT_HUGE); + #endif mi_assert_internal(!page->is_reset); // TODO: push on full queue immediately if it is full? mi_page_queue_t* pq = mi_page_queue(heap, mi_page_block_size(page)); @@ -240,19 +263,26 @@ void _mi_page_reclaim(mi_heap_t* heap, mi_page_t* page) { } // allocate a fresh page from a segment -static mi_page_t* mi_page_fresh_alloc(mi_heap_t* heap, mi_page_queue_t* pq, size_t block_size) { - mi_assert_internal(pq==NULL||mi_heap_contains_queue(heap, pq)); - mi_assert_internal(pq==NULL||block_size == pq->block_size); - mi_page_t* page = _mi_segment_page_alloc(heap, block_size, &heap->tld->segments, &heap->tld->os); +static mi_page_t* mi_page_fresh_alloc(mi_heap_t* heap, mi_page_queue_t* pq, size_t block_size, size_t page_alignment) { + #if !MI_HUGE_PAGE_ABANDON + mi_assert_internal(pq != NULL); + mi_assert_internal(mi_heap_contains_queue(heap, pq)); + mi_assert_internal(page_alignment > 0 || block_size > MI_MEDIUM_OBJ_SIZE_MAX || block_size == pq->block_size); + #endif + mi_page_t* page = _mi_segment_page_alloc(heap, block_size, page_alignment, &heap->tld->segments, &heap->tld->os); if (page == NULL) { // this may be out-of-memory, or an abandoned page was reclaimed (and in our queue) return NULL; } + mi_assert_internal(page_alignment >0 || block_size > MI_MEDIUM_OBJ_SIZE_MAX || _mi_page_segment(page)->kind != MI_SEGMENT_HUGE); + mi_assert_internal(pq!=NULL || page->xblock_size != 0); + mi_assert_internal(pq!=NULL || mi_page_block_size(page) >= block_size); // a fresh page was found, initialize it - mi_assert_internal(pq==NULL || _mi_page_segment(page)->page_kind != MI_PAGE_HUGE); - mi_page_init(heap, page, block_size, heap->tld); - _mi_stat_increase(&heap->tld->stats.pages, 1); - if (pq!=NULL) mi_page_queue_push(heap, pq, page); // huge pages use pq==NULL + const size_t full_block_size = ((pq == NULL || mi_page_queue_is_huge(pq)) ? mi_page_block_size(page) : block_size); // see also: mi_segment_huge_page_alloc + mi_assert_internal(full_block_size >= block_size); + mi_page_init(heap, page, full_block_size, heap->tld); + mi_heap_stat_increase(heap, pages, 1); + if (pq != NULL) { mi_page_queue_push(heap, pq, page); } mi_assert_expensive(_mi_page_is_valid(page)); return page; } @@ -260,7 +290,7 @@ static mi_page_t* mi_page_fresh_alloc(mi_heap_t* heap, mi_page_queue_t* pq, size // Get a fresh page to use static mi_page_t* mi_page_fresh(mi_heap_t* heap, mi_page_queue_t* pq) { mi_assert_internal(mi_heap_contains_queue(heap, pq)); - mi_page_t* page = mi_page_fresh_alloc(heap, pq, pq->block_size); + mi_page_t* page = mi_page_fresh_alloc(heap, pq, pq->block_size, 0); if (page==NULL) return NULL; mi_assert_internal(pq->block_size==mi_page_block_size(page)); mi_assert_internal(pq==mi_page_queue(heap, mi_page_block_size(page))); @@ -271,10 +301,18 @@ static mi_page_t* mi_page_fresh(mi_heap_t* heap, mi_page_queue_t* pq) { Do any delayed frees (put there by other threads if they deallocated in a full page) ----------------------------------------------------------- */ -void _mi_heap_delayed_free(mi_heap_t* heap) { +void _mi_heap_delayed_free_all(mi_heap_t* heap) { + while (!_mi_heap_delayed_free_partial(heap)) { + mi_atomic_yield(); + } +} + +// returns true if all delayed frees were processed +bool _mi_heap_delayed_free_partial(mi_heap_t* heap) { // take over the list (note: no atomic exchange since it is often NULL) mi_block_t* block = mi_atomic_load_ptr_relaxed(mi_block_t, &heap->thread_delayed_free); while (block != NULL && !mi_atomic_cas_ptr_weak_acq_rel(mi_block_t, &heap->thread_delayed_free, &block, NULL)) { /* nothing */ }; + bool all_freed = true; // and free them all while(block != NULL) { @@ -282,7 +320,9 @@ void _mi_heap_delayed_free(mi_heap_t* heap) { // use internal free instead of regular one to keep stats etc correct if (!_mi_free_delayed_block(block)) { // we might already start delayed freeing while another thread has not yet - // reset the delayed_freeing flag; in that case delay it further by reinserting. + // reset the delayed_freeing flag; in that case delay it further by reinserting the current block + // into the delayed free list + all_freed = false; mi_block_t* dfree = mi_atomic_load_ptr_relaxed(mi_block_t, &heap->thread_delayed_free); do { mi_block_set_nextx(heap, block, dfree, heap->keys); @@ -290,6 +330,7 @@ void _mi_heap_delayed_free(mi_heap_t* heap) { } block = next; } + return all_freed; } /* ----------------------------------------------------------- @@ -342,7 +383,7 @@ void _mi_page_abandon(mi_page_t* page, mi_page_queue_t* pq) { mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE); mi_page_set_heap(page, NULL); -#if MI_DEBUG>1 +#if (MI_DEBUG>1) && !MI_TRACK_ENABLED // check there are no references left.. for (mi_block_t* block = (mi_block_t*)pheap->thread_delayed_free; block != NULL; block = mi_block_nextx(pheap, block, pheap->keys)) { mi_assert_internal(_mi_ptr_page(block) != page); @@ -366,9 +407,11 @@ void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force) { // no more aligned blocks in here mi_page_set_has_aligned(page, false); + mi_heap_t* heap = mi_page_heap(page); + // remove from the page list // (no need to do _mi_heap_delayed_free first as all blocks are already free) - mi_segments_tld_t* segments_tld = &mi_page_heap(page)->tld->segments; + mi_segments_tld_t* segments_tld = &heap->tld->segments; mi_page_queue_remove(pq, page); // and free it @@ -376,7 +419,8 @@ void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force) { _mi_segment_page_free(page, force, segments_tld); } -#define MI_MAX_RETIRE_SIZE MI_LARGE_OBJ_SIZE_MAX +// Retire parameters +#define MI_MAX_RETIRE_SIZE (MI_MEDIUM_OBJ_SIZE_MAX) #define MI_RETIRE_CYCLES (8) // Retire a page with no more used blocks @@ -385,11 +429,11 @@ void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force) { // Note: called from `mi_free` and benchmarks often // trigger this due to freeing everything and then // allocating again so careful when changing this. -void _mi_page_retire(mi_page_t* page) { +void _mi_page_retire(mi_page_t* page) mi_attr_noexcept { mi_assert_internal(page != NULL); mi_assert_expensive(_mi_page_is_valid(page)); mi_assert_internal(mi_page_all_free(page)); - + mi_page_set_has_aligned(page, false); // don't retire too often.. @@ -399,10 +443,10 @@ void _mi_page_retire(mi_page_t* page) { // how to check this efficiently though... // for now, we don't retire if it is the only page left of this size class. mi_page_queue_t* pq = mi_page_queue_of(page); - if (mi_likely(page->xblock_size <= MI_MAX_RETIRE_SIZE && !mi_page_is_in_full(page))) { + if mi_likely(page->xblock_size <= MI_MAX_RETIRE_SIZE && !mi_page_queue_is_special(pq)) { // not too large && not full or huge queue? if (pq->last==page && pq->first==page) { // the only page in the queue? mi_stat_counter_increase(_mi_stats_main.page_no_retire,1); - page->retire_expire = (page->xblock_size <= MI_SMALL_OBJ_SIZE_MAX ? MI_RETIRE_CYCLES : MI_RETIRE_CYCLES/4); + page->retire_expire = 1 + (page->xblock_size <= MI_SMALL_OBJ_SIZE_MAX ? MI_RETIRE_CYCLES : MI_RETIRE_CYCLES/4); mi_heap_t* heap = mi_page_heap(page); mi_assert_internal(pq >= heap->pages); const size_t index = pq - heap->pages; @@ -413,7 +457,6 @@ void _mi_page_retire(mi_page_t* page) { return; // dont't free after all } } - _mi_page_free(page, pq, false); } @@ -458,7 +501,7 @@ void _mi_heap_collect_retired(mi_heap_t* heap, bool force) { #define MI_MIN_SLICES (2) static void mi_page_free_list_extend_secure(mi_heap_t* const heap, mi_page_t* const page, const size_t bsize, const size_t extend, mi_stats_t* const stats) { - UNUSED(stats); + MI_UNUSED(stats); #if (MI_SECURE<=2) mi_assert_internal(page->free == NULL); mi_assert_internal(page->local_free == NULL); @@ -516,7 +559,7 @@ static void mi_page_free_list_extend_secure(mi_heap_t* const heap, mi_page_t* co static mi_decl_noinline void mi_page_free_list_extend( mi_page_t* const page, const size_t bsize, const size_t extend, mi_stats_t* const stats) { - UNUSED(stats); + MI_UNUSED(stats); #if (MI_SECURE <= 2) mi_assert_internal(page->free == NULL); mi_assert_internal(page->local_free == NULL); @@ -548,7 +591,7 @@ static mi_decl_noinline void mi_page_free_list_extend( mi_page_t* const page, co #if (MI_SECURE>0) #define MI_MIN_EXTEND (8*MI_SECURE) // extend at least by this many #else -#define MI_MIN_EXTEND (1) +#define MI_MIN_EXTEND (4) #endif // Extend the capacity (up to reserved) by initializing a free list @@ -557,6 +600,7 @@ static mi_decl_noinline void mi_page_free_list_extend( mi_page_t* const page, co // allocations but this did not speed up any benchmark (due to an // extra test in malloc? or cache effects?) static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld) { + MI_UNUSED(tld); mi_assert_expensive(mi_page_is_valid_init(page)); #if (MI_SECURE<=2) mi_assert(page->free == NULL); @@ -566,20 +610,22 @@ static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld) if (page->capacity >= page->reserved) return; size_t page_size; - //uint8_t* page_start = _mi_page_start(_mi_page_segment(page), page, &page_size); mi_stat_counter_increase(tld->stats.pages_extended, 1); // calculate the extend count const size_t bsize = (page->xblock_size < MI_HUGE_BLOCK_SIZE ? page->xblock_size : page_size); size_t extend = page->reserved - page->capacity; + mi_assert_internal(extend > 0); + size_t max_extend = (bsize >= MI_MAX_EXTEND_SIZE ? MI_MIN_EXTEND : MI_MAX_EXTEND_SIZE/(uint32_t)bsize); - if (max_extend < MI_MIN_EXTEND) max_extend = MI_MIN_EXTEND; + if (max_extend < MI_MIN_EXTEND) { max_extend = MI_MIN_EXTEND; } + mi_assert_internal(max_extend > 0); if (extend > max_extend) { // ensure we don't touch memory beyond the page to reduce page commit. // the `lean` benchmark tests this. Going from 1 to 8 increases rss by 50%. - extend = (max_extend==0 ? 1 : max_extend); + extend = max_extend; } mi_assert_internal(extend > 0 && extend + page->capacity <= page->reserved); @@ -611,17 +657,28 @@ static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t block_size, mi mi_assert_internal(block_size > 0); // set fields mi_page_set_heap(page, heap); + page->xblock_size = (block_size < MI_HUGE_BLOCK_SIZE ? (uint32_t)block_size : MI_HUGE_BLOCK_SIZE); // initialize before _mi_segment_page_start size_t page_size; - _mi_segment_page_start(segment, page, block_size, &page_size, NULL); - page->xblock_size = (block_size < MI_HUGE_BLOCK_SIZE ? (uint32_t)block_size : MI_HUGE_BLOCK_SIZE); + const void* page_start = _mi_segment_page_start(segment, page, &page_size); + MI_UNUSED(page_start); + mi_track_mem_noaccess(page_start,page_size); + mi_assert_internal(mi_page_block_size(page) <= page_size); + mi_assert_internal(page_size <= page->slice_count*MI_SEGMENT_SLICE_SIZE); mi_assert_internal(page_size / block_size < (1L<<16)); page->reserved = (uint16_t)(page_size / block_size); - #ifdef MI_ENCODE_FREELIST + mi_assert_internal(page->reserved > 0); + #if (MI_PADDING || MI_ENCODE_FREELIST) page->keys[0] = _mi_heap_random_next(heap); page->keys[1] = _mi_heap_random_next(heap); #endif + #if MI_DEBUG > 0 + page->is_zero = false; // ensure in debug mode we initialize with MI_DEBUG_UNINIT, see issue #501 + #else page->is_zero = page->is_zero_init; + #endif + mi_assert_internal(page->is_committed); + mi_assert_internal(!page->is_reset); mi_assert_internal(page->capacity == 0); mi_assert_internal(page->free == NULL); mi_assert_internal(page->used == 0); @@ -630,7 +687,7 @@ static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t block_size, mi mi_assert_internal(page->prev == NULL); mi_assert_internal(page->retire_expire == 0); mi_assert_internal(!mi_page_has_aligned(page)); - #if (MI_ENCODE_FREELIST) + #if (MI_PADDING || MI_ENCODE_FREELIST) mi_assert_internal(page->keys[0] != 0); mi_assert_internal(page->keys[1] != 0); #endif @@ -650,12 +707,16 @@ static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t block_size, mi static mi_page_t* mi_page_queue_find_free_ex(mi_heap_t* heap, mi_page_queue_t* pq, bool first_try) { // search through the pages in "next fit" order + #if MI_STAT size_t count = 0; + #endif mi_page_t* page = pq->first; while (page != NULL) { mi_page_t* next = page->next; // remember next + #if MI_STAT count++; + #endif // 0. collect freed blocks by us and other threads _mi_page_free_collect(page, false); @@ -680,14 +741,14 @@ static mi_page_t* mi_page_queue_find_free_ex(mi_heap_t* heap, mi_page_queue_t* p page = next; } // for each page - mi_stat_counter_increase(heap->tld->stats.searches, count); + mi_heap_stat_counter_increase(heap, searches, count); if (page == NULL) { - _mi_heap_collect_retired(heap, false); // perhaps make a page available + _mi_heap_collect_retired(heap, false); // perhaps make a page available? page = mi_page_fresh(heap, pq); if (page == NULL && first_try) { // out-of-memory _or_ an abandoned page with free blocks was reclaimed, try once again - page = mi_page_queue_find_free_ex(heap, pq, false); + page = mi_page_queue_find_free_ex(heap, pq, false); } } else { @@ -705,17 +766,17 @@ static inline mi_page_t* mi_find_free_page(mi_heap_t* heap, size_t size) { mi_page_queue_t* pq = mi_page_queue(heap,size); mi_page_t* page = pq->first; if (page != NULL) { - #if (MI_SECURE>=3) // in secure mode, we extend half the time to increase randomness + #if (MI_SECURE>=3) // in secure mode, we extend half the time to increase randomness if (page->capacity < page->reserved && ((_mi_heap_random_next(heap) & 1) == 1)) { mi_page_extend_free(heap, page, heap->tld); mi_assert_internal(mi_page_immediate_available(page)); } - else + else #endif { _mi_page_free_collect(page,false); } - + if (mi_page_immediate_available(page)) { page->retire_expire = 0; return page; // fast path @@ -754,30 +815,46 @@ void mi_register_deferred_free(mi_deferred_free_fun* fn, void* arg) mi_attr_noex General allocation ----------------------------------------------------------- */ -// A huge page is allocated directly without being in a queue. +// Large and huge page allocation. +// Huge pages are allocated directly without being in a queue. // Because huge pages contain just one block, and the segment contains // just that page, we always treat them as abandoned and any thread // that frees the block can free the whole page and segment directly. -static mi_page_t* mi_huge_page_alloc(mi_heap_t* heap, size_t size) { +// Huge pages are also use if the requested alignment is very large (> MI_ALIGNMENT_MAX). +static mi_page_t* mi_large_huge_page_alloc(mi_heap_t* heap, size_t size, size_t page_alignment) { size_t block_size = _mi_os_good_alloc_size(size); - mi_assert_internal(_mi_bin(block_size) == MI_BIN_HUGE); - mi_page_t* page = mi_page_fresh_alloc(heap,NULL,block_size); + mi_assert_internal(mi_bin(block_size) == MI_BIN_HUGE || page_alignment > 0); + bool is_huge = (block_size > MI_LARGE_OBJ_SIZE_MAX || page_alignment > 0); + #if MI_HUGE_PAGE_ABANDON + mi_page_queue_t* pq = (is_huge ? NULL : mi_page_queue(heap, block_size)); + #else + mi_page_queue_t* pq = mi_page_queue(heap, is_huge ? MI_HUGE_BLOCK_SIZE : block_size); // not block_size as that can be low if the page_alignment > 0 + mi_assert_internal(!is_huge || mi_page_queue_is_huge(pq)); + #endif + mi_page_t* page = mi_page_fresh_alloc(heap, pq, block_size, page_alignment); if (page != NULL) { - const size_t bsize = mi_page_block_size(page); // note: not `mi_page_usable_block_size` as `size` includes padding already - mi_assert_internal(bsize >= size); mi_assert_internal(mi_page_immediate_available(page)); - mi_assert_internal(_mi_page_segment(page)->page_kind==MI_PAGE_HUGE); - mi_assert_internal(_mi_page_segment(page)->used==1); - mi_assert_internal(_mi_page_segment(page)->thread_id==0); // abandoned, not in the huge queue - mi_page_set_heap(page, NULL); - - if (bsize > MI_HUGE_OBJ_SIZE_MAX) { - _mi_stat_increase(&heap->tld->stats.giant, bsize); - _mi_stat_counter_increase(&heap->tld->stats.giant_count, 1); + + if (is_huge) { + mi_assert_internal(_mi_page_segment(page)->kind == MI_SEGMENT_HUGE); + mi_assert_internal(_mi_page_segment(page)->used==1); + #if MI_HUGE_PAGE_ABANDON + mi_assert_internal(_mi_page_segment(page)->thread_id==0); // abandoned, not in the huge queue + mi_page_set_heap(page, NULL); + #endif + } + else { + mi_assert_internal(_mi_page_segment(page)->kind != MI_SEGMENT_HUGE); + } + + const size_t bsize = mi_page_usable_block_size(page); // note: not `mi_page_block_size` to account for padding + if (bsize <= MI_LARGE_OBJ_SIZE_MAX) { + mi_heap_stat_increase(heap, large, bsize); + mi_heap_stat_counter_increase(heap, large_count, 1); } else { - _mi_stat_increase(&heap->tld->stats.huge, bsize); - _mi_stat_counter_increase(&heap->tld->stats.huge_count, 1); + mi_heap_stat_increase(heap, huge, bsize); + mi_heap_stat_counter_increase(heap, huge_count, 1); } } return page; @@ -786,54 +863,57 @@ static mi_page_t* mi_huge_page_alloc(mi_heap_t* heap, size_t size) { // Allocate a page // Note: in debug mode the size includes MI_PADDING_SIZE and might have overflowed. -static mi_page_t* mi_find_page(mi_heap_t* heap, size_t size) mi_attr_noexcept { +static mi_page_t* mi_find_page(mi_heap_t* heap, size_t size, size_t huge_alignment) mi_attr_noexcept { // huge allocation? const size_t req_size = size - MI_PADDING_SIZE; // correct for padding_size in case of an overflow on `size` - if (mi_unlikely(req_size > (MI_LARGE_OBJ_SIZE_MAX - MI_PADDING_SIZE) )) { - if (mi_unlikely(req_size > PTRDIFF_MAX)) { // we don't allocate more than PTRDIFF_MAX (see ) + if mi_unlikely(req_size > (MI_MEDIUM_OBJ_SIZE_MAX - MI_PADDING_SIZE) || huge_alignment > 0) { + if mi_unlikely(req_size > PTRDIFF_MAX) { // we don't allocate more than PTRDIFF_MAX (see ) _mi_error_message(EOVERFLOW, "allocation request is too large (%zu bytes)\n", req_size); return NULL; } else { - return mi_huge_page_alloc(heap,size); + return mi_large_huge_page_alloc(heap,size,huge_alignment); } } else { // otherwise find a page with free blocks in our size segregated queues - mi_assert_internal(size >= MI_PADDING_SIZE); + #if MI_PADDING + mi_assert_internal(size >= MI_PADDING_SIZE); + #endif return mi_find_free_page(heap, size); } } // Generic allocation routine if the fast path (`alloc.c:mi_page_malloc`) does not succeed. // Note: in debug mode the size includes MI_PADDING_SIZE and might have overflowed. -void* _mi_malloc_generic(mi_heap_t* heap, size_t size) mi_attr_noexcept +// The `huge_alignment` is normally 0 but is set to a multiple of MI_SEGMENT_SIZE for +// very large requested alignments in which case we use a huge segment. +void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept { mi_assert_internal(heap != NULL); // initialize if necessary - if (mi_unlikely(!mi_heap_is_initialized(heap))) { - mi_thread_init(); // calls `_mi_heap_init` in turn - heap = mi_get_default_heap(); - if (mi_unlikely(!mi_heap_is_initialized(heap))) { return NULL; } + if mi_unlikely(!mi_heap_is_initialized(heap)) { + heap = mi_heap_get_default(); // calls mi_thread_init + if mi_unlikely(!mi_heap_is_initialized(heap)) { return NULL; } } mi_assert_internal(mi_heap_is_initialized(heap)); // call potential deferred free routines _mi_deferred_free(heap, false); - // free delayed frees from other threads - _mi_heap_delayed_free(heap); + // free delayed frees from other threads (but skip contended ones) + _mi_heap_delayed_free_partial(heap); // find (or allocate) a page of the right size - mi_page_t* page = mi_find_page(heap, size); - if (mi_unlikely(page == NULL)) { // first time out of memory, try to collect and retry the allocation once more + mi_page_t* page = mi_find_page(heap, size, huge_alignment); + if mi_unlikely(page == NULL) { // first time out of memory, try to collect and retry the allocation once more mi_heap_collect(heap, true /* force */); - page = mi_find_page(heap, size); + page = mi_find_page(heap, size, huge_alignment); } - if (mi_unlikely(page == NULL)) { // out of memory - const size_t req_size = size - MI_PADDING_SIZE; // correct for padding_size in case of an overflow on `size` + if mi_unlikely(page == NULL) { // out of memory + const size_t req_size = size - MI_PADDING_SIZE; // correct for padding_size in case of an overflow on `size` _mi_error_message(ENOMEM, "unable to allocate memory (%zu bytes)\n", req_size); return NULL; } @@ -841,6 +921,15 @@ void* _mi_malloc_generic(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_assert_internal(mi_page_immediate_available(page)); mi_assert_internal(mi_page_block_size(page) >= size); - // and try again, this time succeeding! (i.e. this should never recurse) - return _mi_page_malloc(heap, page, size); + // and try again, this time succeeding! (i.e. this should never recurse through _mi_page_malloc) + if mi_unlikely(zero && page->xblock_size == 0) { + // note: we cannot call _mi_page_malloc with zeroing for huge blocks; we zero it afterwards in that case. + void* p = _mi_page_malloc(heap, page, size, false); + mi_assert_internal(p != NULL); + _mi_memzero_aligned(p, mi_page_usable_block_size(page)); + return p; + } + else { + return _mi_page_malloc(heap, page, size, zero); + } } diff --git a/Source/mimalloc/src/prim/osx/alloc-override-zone.c b/Source/mimalloc/src/prim/osx/alloc-override-zone.c new file mode 100644 index 000000000..80bcfa939 --- /dev/null +++ b/Source/mimalloc/src/prim/osx/alloc-override-zone.c @@ -0,0 +1,458 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2022, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +#include "mimalloc.h" +#include "mimalloc/internal.h" + +#if defined(MI_MALLOC_OVERRIDE) + +#if !defined(__APPLE__) +#error "this file should only be included on macOS" +#endif + +/* ------------------------------------------------------ + Override system malloc on macOS + This is done through the malloc zone interface. + It seems to be most robust in combination with interposing + though or otherwise we may get zone errors as there are could + be allocations done by the time we take over the + zone. +------------------------------------------------------ */ + +#include +#include +#include // memset +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) +// only available from OSX 10.6 +extern malloc_zone_t* malloc_default_purgeable_zone(void) __attribute__((weak_import)); +#endif + +/* ------------------------------------------------------ + malloc zone members +------------------------------------------------------ */ + +static size_t zone_size(malloc_zone_t* zone, const void* p) { + MI_UNUSED(zone); + if (!mi_is_in_heap_region(p)){ return 0; } // not our pointer, bail out + return mi_usable_size(p); +} + +static void* zone_malloc(malloc_zone_t* zone, size_t size) { + MI_UNUSED(zone); + return mi_malloc(size); +} + +static void* zone_calloc(malloc_zone_t* zone, size_t count, size_t size) { + MI_UNUSED(zone); + return mi_calloc(count, size); +} + +static void* zone_valloc(malloc_zone_t* zone, size_t size) { + MI_UNUSED(zone); + return mi_malloc_aligned(size, _mi_os_page_size()); +} + +static void zone_free(malloc_zone_t* zone, void* p) { + MI_UNUSED(zone); + mi_cfree(p); +} + +static void* zone_realloc(malloc_zone_t* zone, void* p, size_t newsize) { + MI_UNUSED(zone); + return mi_realloc(p, newsize); +} + +static void* zone_memalign(malloc_zone_t* zone, size_t alignment, size_t size) { + MI_UNUSED(zone); + return mi_malloc_aligned(size,alignment); +} + +static void zone_destroy(malloc_zone_t* zone) { + MI_UNUSED(zone); + // todo: ignore for now? +} + +static unsigned zone_batch_malloc(malloc_zone_t* zone, size_t size, void** ps, unsigned count) { + size_t i; + for (i = 0; i < count; i++) { + ps[i] = zone_malloc(zone, size); + if (ps[i] == NULL) break; + } + return i; +} + +static void zone_batch_free(malloc_zone_t* zone, void** ps, unsigned count) { + for(size_t i = 0; i < count; i++) { + zone_free(zone, ps[i]); + ps[i] = NULL; + } +} + +static size_t zone_pressure_relief(malloc_zone_t* zone, size_t size) { + MI_UNUSED(zone); MI_UNUSED(size); + mi_collect(false); + return 0; +} + +static void zone_free_definite_size(malloc_zone_t* zone, void* p, size_t size) { + MI_UNUSED(size); + zone_free(zone,p); +} + +static boolean_t zone_claimed_address(malloc_zone_t* zone, void* p) { + MI_UNUSED(zone); + return mi_is_in_heap_region(p); +} + + +/* ------------------------------------------------------ + Introspection members +------------------------------------------------------ */ + +static kern_return_t intro_enumerator(task_t task, void* p, + unsigned type_mask, vm_address_t zone_address, + memory_reader_t reader, + vm_range_recorder_t recorder) +{ + // todo: enumerate all memory + MI_UNUSED(task); MI_UNUSED(p); MI_UNUSED(type_mask); MI_UNUSED(zone_address); + MI_UNUSED(reader); MI_UNUSED(recorder); + return KERN_SUCCESS; +} + +static size_t intro_good_size(malloc_zone_t* zone, size_t size) { + MI_UNUSED(zone); + return mi_good_size(size); +} + +static boolean_t intro_check(malloc_zone_t* zone) { + MI_UNUSED(zone); + return true; +} + +static void intro_print(malloc_zone_t* zone, boolean_t verbose) { + MI_UNUSED(zone); MI_UNUSED(verbose); + mi_stats_print(NULL); +} + +static void intro_log(malloc_zone_t* zone, void* p) { + MI_UNUSED(zone); MI_UNUSED(p); + // todo? +} + +static void intro_force_lock(malloc_zone_t* zone) { + MI_UNUSED(zone); + // todo? +} + +static void intro_force_unlock(malloc_zone_t* zone) { + MI_UNUSED(zone); + // todo? +} + +static void intro_statistics(malloc_zone_t* zone, malloc_statistics_t* stats) { + MI_UNUSED(zone); + // todo... + stats->blocks_in_use = 0; + stats->size_in_use = 0; + stats->max_size_in_use = 0; + stats->size_allocated = 0; +} + +static boolean_t intro_zone_locked(malloc_zone_t* zone) { + MI_UNUSED(zone); + return false; +} + + +/* ------------------------------------------------------ + At process start, override the default allocator +------------------------------------------------------ */ + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wc99-extensions" +#endif + +static malloc_introspection_t mi_introspect = { + .enumerator = &intro_enumerator, + .good_size = &intro_good_size, + .check = &intro_check, + .print = &intro_print, + .log = &intro_log, + .force_lock = &intro_force_lock, + .force_unlock = &intro_force_unlock, +#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) + .statistics = &intro_statistics, + .zone_locked = &intro_zone_locked, +#endif +}; + +static malloc_zone_t mi_malloc_zone = { + // note: even with designators, the order is important for C++ compilation + //.reserved1 = NULL, + //.reserved2 = NULL, + .size = &zone_size, + .malloc = &zone_malloc, + .calloc = &zone_calloc, + .valloc = &zone_valloc, + .free = &zone_free, + .realloc = &zone_realloc, + .destroy = &zone_destroy, + .zone_name = "mimalloc", + .batch_malloc = &zone_batch_malloc, + .batch_free = &zone_batch_free, + .introspect = &mi_introspect, +#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) + #if defined(MAC_OS_X_VERSION_10_14) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14) + .version = 10, + #else + .version = 9, + #endif + // switch to version 9+ on OSX 10.6 to support memalign. + .memalign = &zone_memalign, + .free_definite_size = &zone_free_definite_size, + .pressure_relief = &zone_pressure_relief, + #if defined(MAC_OS_X_VERSION_10_14) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14) + .claimed_address = &zone_claimed_address, + #endif +#else + .version = 4, +#endif +}; + +#ifdef __cplusplus +} +#endif + + +#if defined(MI_OSX_INTERPOSE) && defined(MI_SHARED_LIB_EXPORT) + +// ------------------------------------------------------ +// Override malloc_xxx and malloc_zone_xxx api's to use only +// our mimalloc zone. Since even the loader uses malloc +// on macOS, this ensures that all allocations go through +// mimalloc (as all calls are interposed). +// The main `malloc`, `free`, etc calls are interposed in `alloc-override.c`, +// Here, we also override macOS specific API's like +// `malloc_zone_calloc` etc. see +// ------------------------------------------------------ + +static inline malloc_zone_t* mi_get_default_zone(void) +{ + static bool init; + if mi_unlikely(!init) { + init = true; + malloc_zone_register(&mi_malloc_zone); // by calling register we avoid a zone error on free (see ) + } + return &mi_malloc_zone; +} + +mi_decl_externc int malloc_jumpstart(uintptr_t cookie); +mi_decl_externc void _malloc_fork_prepare(void); +mi_decl_externc void _malloc_fork_parent(void); +mi_decl_externc void _malloc_fork_child(void); + + +static malloc_zone_t* mi_malloc_create_zone(vm_size_t size, unsigned flags) { + MI_UNUSED(size); MI_UNUSED(flags); + return mi_get_default_zone(); +} + +static malloc_zone_t* mi_malloc_default_zone (void) { + return mi_get_default_zone(); +} + +static malloc_zone_t* mi_malloc_default_purgeable_zone(void) { + return mi_get_default_zone(); +} + +static void mi_malloc_destroy_zone(malloc_zone_t* zone) { + MI_UNUSED(zone); + // nothing. +} + +static kern_return_t mi_malloc_get_all_zones (task_t task, memory_reader_t mr, vm_address_t** addresses, unsigned* count) { + MI_UNUSED(task); MI_UNUSED(mr); + if (addresses != NULL) *addresses = NULL; + if (count != NULL) *count = 0; + return KERN_SUCCESS; +} + +static const char* mi_malloc_get_zone_name(malloc_zone_t* zone) { + return (zone == NULL ? mi_malloc_zone.zone_name : zone->zone_name); +} + +static void mi_malloc_set_zone_name(malloc_zone_t* zone, const char* name) { + MI_UNUSED(zone); MI_UNUSED(name); +} + +static int mi_malloc_jumpstart(uintptr_t cookie) { + MI_UNUSED(cookie); + return 1; // or 0 for no error? +} + +static void mi__malloc_fork_prepare(void) { + // nothing +} +static void mi__malloc_fork_parent(void) { + // nothing +} +static void mi__malloc_fork_child(void) { + // nothing +} + +static void mi_malloc_printf(const char* fmt, ...) { + MI_UNUSED(fmt); +} + +static bool zone_check(malloc_zone_t* zone) { + MI_UNUSED(zone); + return true; +} + +static malloc_zone_t* zone_from_ptr(const void* p) { + MI_UNUSED(p); + return mi_get_default_zone(); +} + +static void zone_log(malloc_zone_t* zone, void* p) { + MI_UNUSED(zone); MI_UNUSED(p); +} + +static void zone_print(malloc_zone_t* zone, bool b) { + MI_UNUSED(zone); MI_UNUSED(b); +} + +static void zone_print_ptr_info(void* p) { + MI_UNUSED(p); +} + +static void zone_register(malloc_zone_t* zone) { + MI_UNUSED(zone); +} + +static void zone_unregister(malloc_zone_t* zone) { + MI_UNUSED(zone); +} + +// use interposing so `DYLD_INSERT_LIBRARIES` works without `DYLD_FORCE_FLAT_NAMESPACE=1` +// See: +struct mi_interpose_s { + const void* replacement; + const void* target; +}; +#define MI_INTERPOSE_FUN(oldfun,newfun) { (const void*)&newfun, (const void*)&oldfun } +#define MI_INTERPOSE_MI(fun) MI_INTERPOSE_FUN(fun,mi_##fun) +#define MI_INTERPOSE_ZONE(fun) MI_INTERPOSE_FUN(malloc_##fun,fun) +__attribute__((used)) static const struct mi_interpose_s _mi_zone_interposes[] __attribute__((section("__DATA, __interpose"))) = +{ + + MI_INTERPOSE_MI(malloc_create_zone), + MI_INTERPOSE_MI(malloc_default_purgeable_zone), + MI_INTERPOSE_MI(malloc_default_zone), + MI_INTERPOSE_MI(malloc_destroy_zone), + MI_INTERPOSE_MI(malloc_get_all_zones), + MI_INTERPOSE_MI(malloc_get_zone_name), + MI_INTERPOSE_MI(malloc_jumpstart), + MI_INTERPOSE_MI(malloc_printf), + MI_INTERPOSE_MI(malloc_set_zone_name), + MI_INTERPOSE_MI(_malloc_fork_child), + MI_INTERPOSE_MI(_malloc_fork_parent), + MI_INTERPOSE_MI(_malloc_fork_prepare), + + MI_INTERPOSE_ZONE(zone_batch_free), + MI_INTERPOSE_ZONE(zone_batch_malloc), + MI_INTERPOSE_ZONE(zone_calloc), + MI_INTERPOSE_ZONE(zone_check), + MI_INTERPOSE_ZONE(zone_free), + MI_INTERPOSE_ZONE(zone_from_ptr), + MI_INTERPOSE_ZONE(zone_log), + MI_INTERPOSE_ZONE(zone_malloc), + MI_INTERPOSE_ZONE(zone_memalign), + MI_INTERPOSE_ZONE(zone_print), + MI_INTERPOSE_ZONE(zone_print_ptr_info), + MI_INTERPOSE_ZONE(zone_realloc), + MI_INTERPOSE_ZONE(zone_register), + MI_INTERPOSE_ZONE(zone_unregister), + MI_INTERPOSE_ZONE(zone_valloc) +}; + + +#else + +// ------------------------------------------------------ +// hook into the zone api's without interposing +// This is the official way of adding an allocator but +// it seems less robust than using interpose. +// ------------------------------------------------------ + +static inline malloc_zone_t* mi_get_default_zone(void) +{ + // The first returned zone is the real default + malloc_zone_t** zones = NULL; + unsigned count = 0; + kern_return_t ret = malloc_get_all_zones(0, NULL, (vm_address_t**)&zones, &count); + if (ret == KERN_SUCCESS && count > 0) { + return zones[0]; + } + else { + // fallback + return malloc_default_zone(); + } +} + +#if defined(__clang__) +__attribute__((constructor(0))) +#else +__attribute__((constructor)) // seems not supported by g++-11 on the M1 +#endif +static void _mi_macos_override_malloc(void) { + malloc_zone_t* purgeable_zone = NULL; + + #if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) + // force the purgeable zone to exist to avoid strange bugs + if (malloc_default_purgeable_zone) { + purgeable_zone = malloc_default_purgeable_zone(); + } + #endif + + // Register our zone. + // thomcc: I think this is still needed to put us in the zone list. + malloc_zone_register(&mi_malloc_zone); + // Unregister the default zone, this makes our zone the new default + // as that was the last registered. + malloc_zone_t *default_zone = mi_get_default_zone(); + // thomcc: Unsure if the next test is *always* false or just false in the + // cases I've tried. I'm also unsure if the code inside is needed. at all + if (default_zone != &mi_malloc_zone) { + malloc_zone_unregister(default_zone); + + // Reregister the default zone so free and realloc in that zone keep working. + malloc_zone_register(default_zone); + } + + // Unregister, and re-register the purgeable_zone to avoid bugs if it occurs + // earlier than the default zone. + if (purgeable_zone != NULL) { + malloc_zone_unregister(purgeable_zone); + malloc_zone_register(purgeable_zone); + } + +} +#endif // MI_OSX_INTERPOSE + +#endif // MI_MALLOC_OVERRIDE diff --git a/Source/mimalloc/src/prim/osx/prim.c b/Source/mimalloc/src/prim/osx/prim.c new file mode 100644 index 000000000..8a2f4e8aa --- /dev/null +++ b/Source/mimalloc/src/prim/osx/prim.c @@ -0,0 +1,9 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +// We use the unix/prim.c with the mmap API on macOSX +#include "../unix/prim.c" diff --git a/Source/mimalloc/src/prim/prim.c b/Source/mimalloc/src/prim/prim.c new file mode 100644 index 000000000..9a597d8eb --- /dev/null +++ b/Source/mimalloc/src/prim/prim.c @@ -0,0 +1,24 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +// Select the implementation of the primitives +// depending on the OS. + +#if defined(_WIN32) +#include "windows/prim.c" // VirtualAlloc (Windows) + +#elif defined(__APPLE__) +#include "osx/prim.c" // macOSX (actually defers to mmap in unix/prim.c) + +#elif defined(__wasi__) +#define MI_USE_SBRK +#include "wasi/prim.c" // memory-grow or sbrk (Wasm) + +#else +#include "unix/prim.c" // mmap() (Linux, macOSX, BSD, Illumnos, Haiku, DragonFly, etc.) + +#endif diff --git a/Source/mimalloc/src/prim/readme.md b/Source/mimalloc/src/prim/readme.md new file mode 100644 index 000000000..380dd3a71 --- /dev/null +++ b/Source/mimalloc/src/prim/readme.md @@ -0,0 +1,9 @@ +## Portability Primitives + +This is the portability layer where all primitives needed from the OS are defined. + +- `include/mimalloc/prim.h`: primitive portability API definition. +- `prim.c`: Selects one of `unix/prim.c`, `wasi/prim.c`, or `windows/prim.c` depending on the host platform + (and on macOS, `osx/prim.c` defers to `unix/prim.c`). + +Note: still work in progress, there may still be places in the sources that still depend on OS ifdef's. \ No newline at end of file diff --git a/Source/mimalloc/src/prim/unix/prim.c b/Source/mimalloc/src/prim/unix/prim.c new file mode 100644 index 000000000..011ffa7cd --- /dev/null +++ b/Source/mimalloc/src/prim/unix/prim.c @@ -0,0 +1,838 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +// This file is included in `src/prim/prim.c` + +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE // ensure mmap flags and syscall are defined +#endif + +#if defined(__sun) +// illumos provides new mman.h api when any of these are defined +// otherwise the old api based on caddr_t which predates the void pointers one. +// stock solaris provides only the former, chose to atomically to discard those +// flags only here rather than project wide tough. +#undef _XOPEN_SOURCE +#undef _POSIX_C_SOURCE +#endif + +#include "mimalloc.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" +#include "mimalloc/prim.h" + +#include // mmap +#include // sysconf + +#if defined(__linux__) + #include + #include + #if defined(__GLIBC__) + #include // linux mmap flags + #else + #include + #endif +#elif defined(__APPLE__) + #include + #if !TARGET_IOS_IPHONE && !TARGET_IOS_SIMULATOR + #include + #endif +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include + #if __FreeBSD_version >= 1200000 + #include + #include + #endif + #include +#endif + +#if !defined(__HAIKU__) && !defined(__APPLE__) && !defined(__CYGWIN__) + #define MI_HAS_SYSCALL_H + #include +#endif + +//------------------------------------------------------------------------------------ +// Use syscalls for some primitives to allow for libraries that override open/read/close etc. +// and do allocation themselves; using syscalls prevents recursion when mimalloc is +// still initializing (issue #713) +//------------------------------------------------------------------------------------ + +#if defined(MI_HAS_SYSCALL_H) && defined(SYS_open) && defined(SYS_close) && defined(SYS_read) && defined(SYS_access) + +static int mi_prim_open(const char* fpath, int open_flags) { + return syscall(SYS_open,fpath,open_flags,0); +} +static ssize_t mi_prim_read(int fd, void* buf, size_t bufsize) { + return syscall(SYS_read,fd,buf,bufsize); +} +static int mi_prim_close(int fd) { + return syscall(SYS_close,fd); +} +static int mi_prim_access(const char *fpath, int mode) { + return syscall(SYS_access,fpath,mode); +} + +#elif !defined(__APPLE__) // avoid unused warnings + +static int mi_prim_open(const char* fpath, int open_flags) { + return open(fpath,open_flags); +} +static ssize_t mi_prim_read(int fd, void* buf, size_t bufsize) { + return read(fd,buf,bufsize); +} +static int mi_prim_close(int fd) { + return close(fd); +} +static int mi_prim_access(const char *fpath, int mode) { + return access(fpath,mode); +} + +#endif + + + +//--------------------------------------------- +// init +//--------------------------------------------- + +static bool unix_detect_overcommit(void) { + bool os_overcommit = true; +#if defined(__linux__) + int fd = mi_prim_open("/proc/sys/vm/overcommit_memory", O_RDONLY); + if (fd >= 0) { + char buf[32]; + ssize_t nread = mi_prim_read(fd, &buf, sizeof(buf)); + mi_prim_close(fd); + // + // 0: heuristic overcommit, 1: always overcommit, 2: never overcommit (ignore NORESERVE) + if (nread >= 1) { + os_overcommit = (buf[0] == '0' || buf[0] == '1'); + } + } +#elif defined(__FreeBSD__) + int val = 0; + size_t olen = sizeof(val); + if (sysctlbyname("vm.overcommit", &val, &olen, NULL, 0) == 0) { + os_overcommit = (val != 0); + } +#else + // default: overcommit is true +#endif + return os_overcommit; +} + +void _mi_prim_mem_init( mi_os_mem_config_t* config ) { + long psize = sysconf(_SC_PAGESIZE); + if (psize > 0) { + config->page_size = (size_t)psize; + config->alloc_granularity = (size_t)psize; + } + config->large_page_size = 2*MI_MiB; // TODO: can we query the OS for this? + config->has_overcommit = unix_detect_overcommit(); + config->must_free_whole = false; // mmap can free in parts +} + + +//--------------------------------------------- +// free +//--------------------------------------------- + +int _mi_prim_free(void* addr, size_t size ) { + bool err = (munmap(addr, size) == -1); + return (err ? errno : 0); +} + + +//--------------------------------------------- +// mmap +//--------------------------------------------- + +static int unix_madvise(void* addr, size_t size, int advice) { + #if defined(__sun) + return madvise((caddr_t)addr, size, advice); // Solaris needs cast (issue #520) + #else + return madvise(addr, size, advice); + #endif +} + +static void* unix_mmap_prim(void* addr, size_t size, size_t try_alignment, int protect_flags, int flags, int fd) { + MI_UNUSED(try_alignment); + void* p = NULL; + #if defined(MAP_ALIGNED) // BSD + if (addr == NULL && try_alignment > 1 && (try_alignment % _mi_os_page_size()) == 0) { + size_t n = mi_bsr(try_alignment); + if (((size_t)1 << n) == try_alignment && n >= 12 && n <= 30) { // alignment is a power of 2 and 4096 <= alignment <= 1GiB + p = mmap(addr, size, protect_flags, flags | MAP_ALIGNED(n), fd, 0); + if (p==MAP_FAILED || !_mi_is_aligned(p,try_alignment)) { + int err = errno; + _mi_warning_message("unable to directly request aligned OS memory (error: %d (0x%x), size: 0x%zx bytes, alignment: 0x%zx, hint address: %p)\n", err, err, size, try_alignment, hint); + } + if (p!=MAP_FAILED) return p; + // fall back to regular mmap + } + } + #elif defined(MAP_ALIGN) // Solaris + if (addr == NULL && try_alignment > 1 && (try_alignment % _mi_os_page_size()) == 0) { + p = mmap((void*)try_alignment, size, protect_flags, flags | MAP_ALIGN, fd, 0); // addr parameter is the required alignment + if (p!=MAP_FAILED) return p; + // fall back to regular mmap + } + #endif + #if (MI_INTPTR_SIZE >= 8) && !defined(MAP_ALIGNED) + // on 64-bit systems, use the virtual address area after 2TiB for 4MiB aligned allocations + if (addr == NULL) { + void* hint = _mi_os_get_aligned_hint(try_alignment, size); + if (hint != NULL) { + p = mmap(hint, size, protect_flags, flags, fd, 0); + if (p==MAP_FAILED || !_mi_is_aligned(p,try_alignment)) { + int err = errno; + _mi_warning_message("unable to directly request hinted aligned OS memory (error: %d (0x%x), size: 0x%zx bytes, alignment: 0x%zx, hint address: %p)\n", err, err, size, try_alignment, hint); + } + if (p!=MAP_FAILED) return p; + // fall back to regular mmap + } + } + #endif + // regular mmap + p = mmap(addr, size, protect_flags, flags, fd, 0); + if (p!=MAP_FAILED) return p; + // failed to allocate + return NULL; +} + +static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protect_flags, bool large_only, bool allow_large, bool* is_large) { + void* p = NULL; + #if !defined(MAP_ANONYMOUS) + #define MAP_ANONYMOUS MAP_ANON + #endif + #if !defined(MAP_NORESERVE) + #define MAP_NORESERVE 0 + #endif + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + int fd = -1; + if (_mi_os_has_overcommit()) { + flags |= MAP_NORESERVE; + } + #if defined(PROT_MAX) + protect_flags |= PROT_MAX(PROT_READ | PROT_WRITE); // BSD + #endif + #if defined(VM_MAKE_TAG) + // macOS: tracking anonymous page with a specific ID. (All up to 98 are taken officially but LLVM sanitizers had taken 99) + int os_tag = (int)mi_option_get(mi_option_os_tag); + if (os_tag < 100 || os_tag > 255) { os_tag = 100; } + fd = VM_MAKE_TAG(os_tag); + #endif + // huge page allocation + if ((large_only || _mi_os_use_large_page(size, try_alignment)) && allow_large) { + static _Atomic(size_t) large_page_try_ok; // = 0; + size_t try_ok = mi_atomic_load_acquire(&large_page_try_ok); + if (!large_only && try_ok > 0) { + // If the OS is not configured for large OS pages, or the user does not have + // enough permission, the `mmap` will always fail (but it might also fail for other reasons). + // Therefore, once a large page allocation failed, we don't try again for `large_page_try_ok` times + // to avoid too many failing calls to mmap. + mi_atomic_cas_strong_acq_rel(&large_page_try_ok, &try_ok, try_ok - 1); + } + else { + int lflags = flags & ~MAP_NORESERVE; // using NORESERVE on huge pages seems to fail on Linux + int lfd = fd; + #ifdef MAP_ALIGNED_SUPER + lflags |= MAP_ALIGNED_SUPER; + #endif + #ifdef MAP_HUGETLB + lflags |= MAP_HUGETLB; + #endif + #ifdef MAP_HUGE_1GB + static bool mi_huge_pages_available = true; + if ((size % MI_GiB) == 0 && mi_huge_pages_available) { + lflags |= MAP_HUGE_1GB; + } + else + #endif + { + #ifdef MAP_HUGE_2MB + lflags |= MAP_HUGE_2MB; + #endif + } + #ifdef VM_FLAGS_SUPERPAGE_SIZE_2MB + lfd |= VM_FLAGS_SUPERPAGE_SIZE_2MB; + #endif + if (large_only || lflags != flags) { + // try large OS page allocation + *is_large = true; + p = unix_mmap_prim(addr, size, try_alignment, protect_flags, lflags, lfd); + #ifdef MAP_HUGE_1GB + if (p == NULL && (lflags & MAP_HUGE_1GB) != 0) { + mi_huge_pages_available = false; // don't try huge 1GiB pages again + _mi_warning_message("unable to allocate huge (1GiB) page, trying large (2MiB) pages instead (errno: %i)\n", errno); + lflags = ((lflags & ~MAP_HUGE_1GB) | MAP_HUGE_2MB); + p = unix_mmap_prim(addr, size, try_alignment, protect_flags, lflags, lfd); + } + #endif + if (large_only) return p; + if (p == NULL) { + mi_atomic_store_release(&large_page_try_ok, (size_t)8); // on error, don't try again for the next N allocations + } + } + } + } + // regular allocation + if (p == NULL) { + *is_large = false; + p = unix_mmap_prim(addr, size, try_alignment, protect_flags, flags, fd); + if (p != NULL) { + #if defined(MADV_HUGEPAGE) + // Many Linux systems don't allow MAP_HUGETLB but they support instead + // transparent huge pages (THP). Generally, it is not required to call `madvise` with MADV_HUGE + // though since properly aligned allocations will already use large pages if available + // in that case -- in particular for our large regions (in `memory.c`). + // However, some systems only allow THP if called with explicit `madvise`, so + // when large OS pages are enabled for mimalloc, we call `madvise` anyways. + if (allow_large && _mi_os_use_large_page(size, try_alignment)) { + if (unix_madvise(p, size, MADV_HUGEPAGE) == 0) { + *is_large = true; // possibly + }; + } + #elif defined(__sun) + if (allow_large && _mi_os_use_large_page(size, try_alignment)) { + struct memcntl_mha cmd = {0}; + cmd.mha_pagesize = large_os_page_size; + cmd.mha_cmd = MHA_MAPSIZE_VA; + if (memcntl((caddr_t)p, size, MC_HAT_ADVISE, (caddr_t)&cmd, 0, 0) == 0) { + *is_large = true; + } + } + #endif + } + } + return p; +} + +// Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned. +int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, void** addr) { + mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0); + mi_assert_internal(commit || !allow_large); + mi_assert_internal(try_alignment > 0); + + int protect_flags = (commit ? (PROT_WRITE | PROT_READ) : PROT_NONE); + *addr = unix_mmap(NULL, size, try_alignment, protect_flags, false, allow_large, is_large); + return (*addr != NULL ? 0 : errno); +} + + +//--------------------------------------------- +// Commit/Reset +//--------------------------------------------- + +static void unix_mprotect_hint(int err) { + #if defined(__linux__) && (MI_SECURE>=2) // guard page around every mimalloc page + if (err == ENOMEM) { + _mi_warning_message("The next warning may be caused by a low memory map limit.\n" + " On Linux this is controlled by the vm.max_map_count -- maybe increase it?\n" + " For example: sudo sysctl -w vm.max_map_count=262144\n"); + } + #else + MI_UNUSED(err); + #endif +} + + +int _mi_prim_commit(void* start, size_t size, bool commit) { + /* + #if 0 && defined(MAP_FIXED) && !defined(__APPLE__) + // Linux: disabled for now as mmap fixed seems much more expensive than MADV_DONTNEED (and splits VMA's?) + if (commit) { + // commit: just change the protection + err = mprotect(start, csize, (PROT_READ | PROT_WRITE)); + if (err != 0) { err = errno; } + } + else { + // decommit: use mmap with MAP_FIXED to discard the existing memory (and reduce rss) + const int fd = mi_unix_mmap_fd(); + void* p = mmap(start, csize, PROT_NONE, (MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE), fd, 0); + if (p != start) { err = errno; } + } + #else + */ + int err = 0; + if (commit) { + // commit: ensure we can access the area + err = mprotect(start, size, (PROT_READ | PROT_WRITE)); + if (err != 0) { err = errno; } + } + else { + #if defined(MADV_DONTNEED) && MI_DEBUG == 0 && MI_SECURE == 0 + // decommit: use MADV_DONTNEED as it decreases rss immediately (unlike MADV_FREE) + // (on the other hand, MADV_FREE would be good enough.. it is just not reflected in the stats :-( ) + err = unix_madvise(start, size, MADV_DONTNEED); + #else + // decommit: just disable access (also used in debug and secure mode to trap on illegal access) + err = mprotect(start, size, PROT_NONE); + if (err != 0) { err = errno; } + #endif + } + unix_mprotect_hint(err); + return err; +} + +int _mi_prim_reset(void* start, size_t size) { + #if defined(MADV_FREE) + static _Atomic(size_t) advice = MI_ATOMIC_VAR_INIT(MADV_FREE); + int oadvice = (int)mi_atomic_load_relaxed(&advice); + int err; + while ((err = unix_madvise(start, size, oadvice)) != 0 && errno == EAGAIN) { errno = 0; }; + if (err != 0 && errno == EINVAL && oadvice == MADV_FREE) { + // if MADV_FREE is not supported, fall back to MADV_DONTNEED from now on + mi_atomic_store_release(&advice, (size_t)MADV_DONTNEED); + err = unix_madvise(start, size, MADV_DONTNEED); + } + #else + int err = unix_madvise(start, size, MADV_DONTNEED); + #endif + return err; +} + +int _mi_prim_protect(void* start, size_t size, bool protect) { + int err = mprotect(start, size, protect ? PROT_NONE : (PROT_READ | PROT_WRITE)); + if (err != 0) { err = errno; } + unix_mprotect_hint(err); + return err; +} + + + +//--------------------------------------------- +// Huge page allocation +//--------------------------------------------- + +#if (MI_INTPTR_SIZE >= 8) && !defined(__HAIKU__) && !defined(__CYGWIN__) + +#ifndef MPOL_PREFERRED +#define MPOL_PREFERRED 1 +#endif + +#if defined(MI_HAS_SYSCALL_H) && defined(SYS_mbind) +static long mi_prim_mbind(void* start, unsigned long len, unsigned long mode, const unsigned long* nmask, unsigned long maxnode, unsigned flags) { + return syscall(SYS_mbind, start, len, mode, nmask, maxnode, flags); +} +#else +static long mi_prim_mbind(void* start, unsigned long len, unsigned long mode, const unsigned long* nmask, unsigned long maxnode, unsigned flags) { + MI_UNUSED(start); MI_UNUSED(len); MI_UNUSED(mode); MI_UNUSED(nmask); MI_UNUSED(maxnode); MI_UNUSED(flags); + return 0; +} +#endif + +int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, void** addr) { + bool is_large = true; + *addr = unix_mmap(hint_addr, size, MI_SEGMENT_SIZE, PROT_READ | PROT_WRITE, true, true, &is_large); + if (*addr != NULL && numa_node >= 0 && numa_node < 8*MI_INTPTR_SIZE) { // at most 64 nodes + unsigned long numa_mask = (1UL << numa_node); + // TODO: does `mbind` work correctly for huge OS pages? should we + // use `set_mempolicy` before calling mmap instead? + // see: + long err = mi_prim_mbind(*addr, size, MPOL_PREFERRED, &numa_mask, 8*MI_INTPTR_SIZE, 0); + if (err != 0) { + err = errno; + _mi_warning_message("failed to bind huge (1GiB) pages to numa node %d (error: %d (0x%x))\n", numa_node, err, err); + } + } + return (*addr != NULL ? 0 : errno); +} + +#else + +int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, void** addr) { + MI_UNUSED(hint_addr); MI_UNUSED(size); MI_UNUSED(numa_node); + *addr = NULL; + return ENOMEM; +} + +#endif + +//--------------------------------------------- +// NUMA nodes +//--------------------------------------------- + +#if defined(__linux__) + +#include // snprintf + +size_t _mi_prim_numa_node(void) { + #if defined(MI_HAS_SYSCALL_H) && defined(SYS_getcpu) + unsigned long node = 0; + unsigned long ncpu = 0; + long err = syscall(SYS_getcpu, &ncpu, &node, NULL); + if (err != 0) return 0; + return node; + #else + return 0; + #endif +} + +size_t _mi_prim_numa_node_count(void) { + char buf[128]; + unsigned node = 0; + for(node = 0; node < 256; node++) { + // enumerate node entries -- todo: it there a more efficient way to do this? (but ensure there is no allocation) + snprintf(buf, 127, "/sys/devices/system/node/node%u", node + 1); + if (mi_prim_access(buf,R_OK) != 0) break; + } + return (node+1); +} + +#elif defined(__FreeBSD__) && __FreeBSD_version >= 1200000 + +size_t _mi_prim_numa_node(void) { + domainset_t dom; + size_t node; + int policy; + if (cpuset_getdomain(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1, sizeof(dom), &dom, &policy) == -1) return 0ul; + for (node = 0; node < MAXMEMDOM; node++) { + if (DOMAINSET_ISSET(node, &dom)) return node; + } + return 0ul; +} + +size_t _mi_prim_numa_node_count(void) { + size_t ndomains = 0; + size_t len = sizeof(ndomains); + if (sysctlbyname("vm.ndomains", &ndomains, &len, NULL, 0) == -1) return 0ul; + return ndomains; +} + +#elif defined(__DragonFly__) + +size_t _mi_prim_numa_node(void) { + // TODO: DragonFly does not seem to provide any userland means to get this information. + return 0ul; +} + +size_t _mi_prim_numa_node_count(void) { + size_t ncpus = 0, nvirtcoresperphys = 0; + size_t len = sizeof(size_t); + if (sysctlbyname("hw.ncpu", &ncpus, &len, NULL, 0) == -1) return 0ul; + if (sysctlbyname("hw.cpu_topology_ht_ids", &nvirtcoresperphys, &len, NULL, 0) == -1) return 0ul; + return nvirtcoresperphys * ncpus; +} + +#else + +size_t _mi_prim_numa_node(void) { + return 0; +} + +size_t _mi_prim_numa_node_count(void) { + return 1; +} + +#endif + +// ---------------------------------------------------------------- +// Clock +// ---------------------------------------------------------------- + +#include + +#if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) + +mi_msecs_t _mi_prim_clock_now(void) { + struct timespec t; + #ifdef CLOCK_MONOTONIC + clock_gettime(CLOCK_MONOTONIC, &t); + #else + clock_gettime(CLOCK_REALTIME, &t); + #endif + return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); +} + +#else + +// low resolution timer +mi_msecs_t _mi_prim_clock_now(void) { + #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0) + return (mi_msecs_t)clock(); + #elif (CLOCKS_PER_SEC < 1000) + return (mi_msecs_t)clock() * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); + #else + return (mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); + #endif +} + +#endif + + + + +//---------------------------------------------------------------- +// Process info +//---------------------------------------------------------------- + +#if defined(__unix__) || defined(__unix) || defined(unix) || defined(__APPLE__) || defined(__HAIKU__) +#include +#include +#include + +#if defined(__APPLE__) +#include +#endif + +#if defined(__HAIKU__) +#include +#endif + +static mi_msecs_t timeval_secs(const struct timeval* tv) { + return ((mi_msecs_t)tv->tv_sec * 1000L) + ((mi_msecs_t)tv->tv_usec / 1000L); +} + +void _mi_prim_process_info(mi_process_info_t* pinfo) +{ + struct rusage rusage; + getrusage(RUSAGE_SELF, &rusage); + pinfo->utime = timeval_secs(&rusage.ru_utime); + pinfo->stime = timeval_secs(&rusage.ru_stime); +#if !defined(__HAIKU__) + pinfo->page_faults = rusage.ru_majflt; +#endif +#if defined(__HAIKU__) + // Haiku does not have (yet?) a way to + // get these stats per process + thread_info tid; + area_info mem; + ssize_t c; + get_thread_info(find_thread(0), &tid); + while (get_next_area_info(tid.team, &c, &mem) == B_OK) { + pinfo->peak_rss += mem.ram_size; + } + pinfo->page_faults = 0; +#elif defined(__APPLE__) + pinfo->peak_rss = rusage.ru_maxrss; // macos reports in bytes + struct mach_task_basic_info info; + mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; + if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount) == KERN_SUCCESS) { + pinfo->current_rss = (size_t)info.resident_size; + } +#else + pinfo->peak_rss = rusage.ru_maxrss * 1024; // Linux/BSD report in KiB +#endif + // use defaults for commit +} + +#else + +#ifndef __wasi__ +// WebAssembly instances are not processes +#pragma message("define a way to get process info") +#endif + +void _mi_prim_process_info(mi_process_info_t* pinfo) +{ + // use defaults + MI_UNUSED(pinfo); +} + +#endif + + +//---------------------------------------------------------------- +// Output +//---------------------------------------------------------------- + +void _mi_prim_out_stderr( const char* msg ) { + fputs(msg,stderr); +} + + +//---------------------------------------------------------------- +// Environment +//---------------------------------------------------------------- + +#if !defined(MI_USE_ENVIRON) || (MI_USE_ENVIRON!=0) +// On Posix systemsr use `environ` to access environment variables +// even before the C runtime is initialized. +#if defined(__APPLE__) && defined(__has_include) && __has_include() +#include +static char** mi_get_environ(void) { + return (*_NSGetEnviron()); +} +#else +extern char** environ; +static char** mi_get_environ(void) { + return environ; +} +#endif +bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { + if (name==NULL) return false; + const size_t len = _mi_strlen(name); + if (len == 0) return false; + char** env = mi_get_environ(); + if (env == NULL) return false; + // compare up to 10000 entries + for (int i = 0; i < 10000 && env[i] != NULL; i++) { + const char* s = env[i]; + if (_mi_strnicmp(name, s, len) == 0 && s[len] == '=') { // case insensitive + // found it + _mi_strlcpy(result, s + len + 1, result_size); + return true; + } + } + return false; +} +#else +// fallback: use standard C `getenv` but this cannot be used while initializing the C runtime +bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { + // cannot call getenv() when still initializing the C runtime. + if (_mi_preloading()) return false; + const char* s = getenv(name); + if (s == NULL) { + // we check the upper case name too. + char buf[64+1]; + size_t len = _mi_strnlen(name,sizeof(buf)-1); + for (size_t i = 0; i < len; i++) { + buf[i] = _mi_toupper(name[i]); + } + buf[len] = 0; + s = getenv(buf); + } + if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return false; + _mi_strlcpy(result, s, result_size); + return true; +} +#endif // !MI_USE_ENVIRON + + +//---------------------------------------------------------------- +// Random +//---------------------------------------------------------------- + +#if defined(__APPLE__) + +#include +#if defined(MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 +#include +#include +#endif +bool _mi_prim_random_buf(void* buf, size_t buf_len) { + #if defined(MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15 + // We prefere CCRandomGenerateBytes as it returns an error code while arc4random_buf + // may fail silently on macOS. See PR #390, and + return (CCRandomGenerateBytes(buf, buf_len) == kCCSuccess); + #else + // fall back on older macOS + arc4random_buf(buf, buf_len); + return true; + #endif +} + +#elif defined(__ANDROID__) || defined(__DragonFly__) || \ + defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ + defined(__sun) + +#include +bool _mi_prim_random_buf(void* buf, size_t buf_len) { + arc4random_buf(buf, buf_len); + return true; +} + +#elif defined(__linux__) || defined(__HAIKU__) + +#include +#include +#include +#include + +bool _mi_prim_random_buf(void* buf, size_t buf_len) { + // Modern Linux provides `getrandom` but different distributions either use `sys/random.h` or `linux/random.h` + // and for the latter the actual `getrandom` call is not always defined. + // (see ) + // We therefore use a syscall directly and fall back dynamically to /dev/urandom when needed. + #if defined(MI_HAS_SYSCALL_H) && defined(SYS_getrandom) + #ifndef GRND_NONBLOCK + #define GRND_NONBLOCK (1) + #endif + static _Atomic(uintptr_t) no_getrandom; // = 0 + if (mi_atomic_load_acquire(&no_getrandom)==0) { + ssize_t ret = syscall(SYS_getrandom, buf, buf_len, GRND_NONBLOCK); + if (ret >= 0) return (buf_len == (size_t)ret); + if (errno != ENOSYS) return false; + mi_atomic_store_release(&no_getrandom, (uintptr_t)1); // don't call again, and fall back to /dev/urandom + } + #endif + int flags = O_RDONLY; + #if defined(O_CLOEXEC) + flags |= O_CLOEXEC; + #endif + int fd = mi_prim_open("/dev/urandom", flags); + if (fd < 0) return false; + size_t count = 0; + while(count < buf_len) { + ssize_t ret = mi_prim_read(fd, (char*)buf + count, buf_len - count); + if (ret<=0) { + if (errno!=EAGAIN && errno!=EINTR) break; + } + else { + count += ret; + } + } + mi_prim_close(fd); + return (count==buf_len); +} + +#else + +bool _mi_prim_random_buf(void* buf, size_t buf_len) { + return false; +} + +#endif + + +//---------------------------------------------------------------- +// Thread init/done +//---------------------------------------------------------------- + +#if defined(MI_USE_PTHREADS) + +// use pthread local storage keys to detect thread ending +// (and used with MI_TLS_PTHREADS for the default heap) +pthread_key_t _mi_heap_default_key = (pthread_key_t)(-1); + +static void mi_pthread_done(void* value) { + if (value!=NULL) { + _mi_thread_done((mi_heap_t*)value); + } +} + +void _mi_prim_thread_init_auto_done(void) { + mi_assert_internal(_mi_heap_default_key == (pthread_key_t)(-1)); + pthread_key_create(&_mi_heap_default_key, &mi_pthread_done); +} + +void _mi_prim_thread_done_auto_done(void) { + // nothing to do +} + +void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { + if (_mi_heap_default_key != (pthread_key_t)(-1)) { // can happen during recursive invocation on freeBSD + pthread_setspecific(_mi_heap_default_key, heap); + } +} + +#else + +void _mi_prim_thread_init_auto_done(void) { + // nothing +} + +void _mi_prim_thread_done_auto_done(void) { + // nothing +} + +void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { + MI_UNUSED(heap); +} + +#endif diff --git a/Source/mimalloc/src/prim/wasi/prim.c b/Source/mimalloc/src/prim/wasi/prim.c new file mode 100644 index 000000000..cb3ce1a7f --- /dev/null +++ b/Source/mimalloc/src/prim/wasi/prim.c @@ -0,0 +1,265 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +// This file is included in `src/prim/prim.c` + +#include "mimalloc.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" +#include "mimalloc/prim.h" + +//--------------------------------------------- +// Initialize +//--------------------------------------------- + +void _mi_prim_mem_init( mi_os_mem_config_t* config ) { + config->page_size = 64*MI_KiB; // WebAssembly has a fixed page size: 64KiB + config->alloc_granularity = 16; + config->has_overcommit = false; + config->must_free_whole = true; +} + +//--------------------------------------------- +// Free +//--------------------------------------------- + +int _mi_prim_free(void* addr, size_t size ) { + MI_UNUSED(addr); MI_UNUSED(size); + // wasi heap cannot be shrunk + return 0; +} + + +//--------------------------------------------- +// Allocation: sbrk or memory_grow +//--------------------------------------------- + +#if defined(MI_USE_SBRK) + static void* mi_memory_grow( size_t size ) { + void* p = sbrk(size); + if (p == (void*)(-1)) return NULL; + #if !defined(__wasi__) // on wasi this is always zero initialized already (?) + memset(p,0,size); + #endif + return p; + } +#elif defined(__wasi__) + static void* mi_memory_grow( size_t size ) { + size_t base = (size > 0 ? __builtin_wasm_memory_grow(0,_mi_divide_up(size, _mi_os_page_size())) + : __builtin_wasm_memory_size(0)); + if (base == SIZE_MAX) return NULL; + return (void*)(base * _mi_os_page_size()); + } +#endif + +#if defined(MI_USE_PTHREADS) +static pthread_mutex_t mi_heap_grow_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +static void* mi_prim_mem_grow(size_t size, size_t try_alignment) { + void* p = NULL; + if (try_alignment <= 1) { + // `sbrk` is not thread safe in general so try to protect it (we could skip this on WASM but leave it in for now) + #if defined(MI_USE_PTHREADS) + pthread_mutex_lock(&mi_heap_grow_mutex); + #endif + p = mi_memory_grow(size); + #if defined(MI_USE_PTHREADS) + pthread_mutex_unlock(&mi_heap_grow_mutex); + #endif + } + else { + void* base = NULL; + size_t alloc_size = 0; + // to allocate aligned use a lock to try to avoid thread interaction + // between getting the current size and actual allocation + // (also, `sbrk` is not thread safe in general) + #if defined(MI_USE_PTHREADS) + pthread_mutex_lock(&mi_heap_grow_mutex); + #endif + { + void* current = mi_memory_grow(0); // get current size + if (current != NULL) { + void* aligned_current = mi_align_up_ptr(current, try_alignment); // and align from there to minimize wasted space + alloc_size = _mi_align_up( ((uint8_t*)aligned_current - (uint8_t*)current) + size, _mi_os_page_size()); + base = mi_memory_grow(alloc_size); + } + } + #if defined(MI_USE_PTHREADS) + pthread_mutex_unlock(&mi_heap_grow_mutex); + #endif + if (base != NULL) { + p = mi_align_up_ptr(base, try_alignment); + if ((uint8_t*)p + size > (uint8_t*)base + alloc_size) { + // another thread used wasm_memory_grow/sbrk in-between and we do not have enough + // space after alignment. Give up (and waste the space as we cannot shrink :-( ) + // (in `mi_os_mem_alloc_aligned` this will fall back to overallocation to align) + p = NULL; + } + } + } + /* + if (p == NULL) { + _mi_warning_message("unable to allocate sbrk/wasm_memory_grow OS memory (%zu bytes, %zu alignment)\n", size, try_alignment); + errno = ENOMEM; + return NULL; + } + */ + mi_assert_internal( p == NULL || try_alignment == 0 || (uintptr_t)p % try_alignment == 0 ); + return p; +} + +// Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned. +int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, void** addr) { + MI_UNUSED(allow_large); MI_UNUSED(commit); + *is_large = false; + *addr = mi_prim_mem_grow(size, try_alignment); + return (*addr != NULL ? 0 : ENOMEM); +} + + +//--------------------------------------------- +// Commit/Reset/Protect +//--------------------------------------------- + +int _mi_prim_commit(void* addr, size_t size, bool commit) { + MI_UNUSED(addr); MI_UNUSED(size); MI_UNUSED(commit); + return 0; +} + +int _mi_prim_reset(void* addr, size_t size) { + MI_UNUSED(addr); MI_UNUSED(size); + return 0; +} + +int _mi_prim_protect(void* addr, size_t size, bool protect) { + MI_UNUSED(addr); MI_UNUSED(size); MI_UNUSED(protect); + return 0; +} + + +//--------------------------------------------- +// Huge pages and NUMA nodes +//--------------------------------------------- + +int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, void** addr) { + MI_UNUSED(hint_addr); MI_UNUSED(size); MI_UNUSED(numa_node); + *addr = NULL; + return ENOSYS; +} + +size_t _mi_prim_numa_node(void) { + return 0; +} + +size_t _mi_prim_numa_node_count(void) { + return 1; +} + + +//---------------------------------------------------------------- +// Clock +//---------------------------------------------------------------- + +#include + +#if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) + +mi_msecs_t _mi_prim_clock_now(void) { + struct timespec t; + #ifdef CLOCK_MONOTONIC + clock_gettime(CLOCK_MONOTONIC, &t); + #else + clock_gettime(CLOCK_REALTIME, &t); + #endif + return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); +} + +#else + +// low resolution timer +mi_msecs_t _mi_prim_clock_now(void) { + #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0) + return (mi_msecs_t)clock(); + #elif (CLOCKS_PER_SEC < 1000) + return (mi_msecs_t)clock() * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); + #else + return (mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); + #endif +} + +#endif + + +//---------------------------------------------------------------- +// Process info +//---------------------------------------------------------------- + +void _mi_prim_process_info(mi_process_info_t* pinfo) +{ + // use defaults + MI_UNUSED(pinfo); +} + + +//---------------------------------------------------------------- +// Output +//---------------------------------------------------------------- + +void _mi_prim_out_stderr( const char* msg ) { + fputs(msg,stderr); +} + + +//---------------------------------------------------------------- +// Environment +//---------------------------------------------------------------- + +bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { + // cannot call getenv() when still initializing the C runtime. + if (_mi_preloading()) return false; + const char* s = getenv(name); + if (s == NULL) { + // we check the upper case name too. + char buf[64+1]; + size_t len = _mi_strnlen(name,sizeof(buf)-1); + for (size_t i = 0; i < len; i++) { + buf[i] = _mi_toupper(name[i]); + } + buf[len] = 0; + s = getenv(buf); + } + if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return false; + _mi_strlcpy(result, s, result_size); + return true; +} + + +//---------------------------------------------------------------- +// Random +//---------------------------------------------------------------- + +bool _mi_prim_random_buf(void* buf, size_t buf_len) { + return false; +} + + +//---------------------------------------------------------------- +// Thread init/done +//---------------------------------------------------------------- + +void _mi_prim_thread_init_auto_done(void) { + // nothing +} + +void _mi_prim_thread_done_auto_done(void) { + // nothing +} + +void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { + MI_UNUSED(heap); +} diff --git a/Source/mimalloc/src/prim/windows/etw-mimalloc.wprp b/Source/mimalloc/src/prim/windows/etw-mimalloc.wprp new file mode 100644 index 000000000..b00cd7adf --- /dev/null +++ b/Source/mimalloc/src/prim/windows/etw-mimalloc.wprp @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/mimalloc/src/prim/windows/etw.h b/Source/mimalloc/src/prim/windows/etw.h new file mode 100644 index 000000000..4e0a092a1 --- /dev/null +++ b/Source/mimalloc/src/prim/windows/etw.h @@ -0,0 +1,905 @@ +//**********************************************************************` +//* This is an include file generated by Message Compiler. *` +//* *` +//* Copyright (c) Microsoft Corporation. All Rights Reserved. *` +//**********************************************************************` +#pragma once + +//***************************************************************************** +// +// Notes on the ETW event code generated by MC: +// +// - Structures and arrays of structures are treated as an opaque binary blob. +// The caller is responsible for packing the data for the structure into a +// single region of memory, with no padding between values. The macro will +// have an extra parameter for the length of the blob. +// - Arrays of nul-terminated strings must be packed by the caller into a +// single binary blob containing the correct number of strings, with a nul +// after each string. The size of the blob is specified in characters, and +// includes the final nul. +// - Arrays of SID are treated as a single binary blob. The caller is +// responsible for packing the SID values into a single region of memory with +// no padding. +// - The length attribute on the data element in the manifest is significant +// for values with intype win:UnicodeString, win:AnsiString, or win:Binary. +// The length attribute must be specified for win:Binary, and is optional for +// win:UnicodeString and win:AnsiString (if no length is given, the strings +// are assumed to be nul-terminated). For win:UnicodeString, the length is +// measured in characters, not bytes. +// - For an array of win:UnicodeString, win:AnsiString, or win:Binary, the +// length attribute applies to every value in the array, so every value in +// the array must have the same length. The values in the array are provided +// to the macro via a single pointer -- the caller is responsible for packing +// all of the values into a single region of memory with no padding between +// values. +// - Values of type win:CountedUnicodeString, win:CountedAnsiString, and +// win:CountedBinary can be generated and collected on Vista or later. +// However, they may not decode properly without the Windows 10 2018 Fall +// Update. +// - Arrays of type win:CountedUnicodeString, win:CountedAnsiString, and +// win:CountedBinary must be packed by the caller into a single region of +// memory. The format for each item is a UINT16 byte-count followed by that +// many bytes of data. When providing the array to the generated macro, you +// must provide the total size of the packed array data, including the UINT16 +// sizes for each item. In the case of win:CountedUnicodeString, the data +// size is specified in WCHAR (16-bit) units. In the case of +// win:CountedAnsiString and win:CountedBinary, the data size is specified in +// bytes. +// +//***************************************************************************** + +#include +#include +#include + +#ifndef ETW_INLINE + #ifdef _ETW_KM_ + // In kernel mode, save stack space by never inlining templates. + #define ETW_INLINE DECLSPEC_NOINLINE __inline + #else + // In user mode, save code size by inlining templates as appropriate. + #define ETW_INLINE __inline + #endif +#endif // ETW_INLINE + +#if defined(__cplusplus) +extern "C" { +#endif + +// +// MCGEN_DISABLE_PROVIDER_CODE_GENERATION macro: +// Define this macro to have the compiler skip the generated functions in this +// header. +// +#ifndef MCGEN_DISABLE_PROVIDER_CODE_GENERATION + +// +// MCGEN_USE_KERNEL_MODE_APIS macro: +// Controls whether the generated code uses kernel-mode or user-mode APIs. +// - Set to 0 to use Windows user-mode APIs such as EventRegister. +// - Set to 1 to use Windows kernel-mode APIs such as EtwRegister. +// Default is based on whether the _ETW_KM_ macro is defined (i.e. by wdm.h). +// Note that the APIs can also be overridden directly, e.g. by setting the +// MCGEN_EVENTWRITETRANSFER or MCGEN_EVENTREGISTER macros. +// +#ifndef MCGEN_USE_KERNEL_MODE_APIS + #ifdef _ETW_KM_ + #define MCGEN_USE_KERNEL_MODE_APIS 1 + #else + #define MCGEN_USE_KERNEL_MODE_APIS 0 + #endif +#endif // MCGEN_USE_KERNEL_MODE_APIS + +// +// MCGEN_HAVE_EVENTSETINFORMATION macro: +// Controls how McGenEventSetInformation uses the EventSetInformation API. +// - Set to 0 to disable the use of EventSetInformation +// (McGenEventSetInformation will always return an error). +// - Set to 1 to directly invoke MCGEN_EVENTSETINFORMATION. +// - Set to 2 to to locate EventSetInformation at runtime via GetProcAddress +// (user-mode) or MmGetSystemRoutineAddress (kernel-mode). +// Default is determined as follows: +// - If MCGEN_EVENTSETINFORMATION has been customized, set to 1 +// (i.e. use MCGEN_EVENTSETINFORMATION). +// - Else if the target OS version has EventSetInformation, set to 1 +// (i.e. use MCGEN_EVENTSETINFORMATION). +// - Else set to 2 (i.e. try to dynamically locate EventSetInformation). +// Note that an McGenEventSetInformation function will only be generated if one +// or more provider in a manifest has provider traits. +// +#ifndef MCGEN_HAVE_EVENTSETINFORMATION + #ifdef MCGEN_EVENTSETINFORMATION // if MCGEN_EVENTSETINFORMATION has been customized, + #define MCGEN_HAVE_EVENTSETINFORMATION 1 // directly invoke MCGEN_EVENTSETINFORMATION(...). + #elif MCGEN_USE_KERNEL_MODE_APIS // else if using kernel-mode APIs, + #if NTDDI_VERSION >= 0x06040000 // if target OS is Windows 10 or later, + #define MCGEN_HAVE_EVENTSETINFORMATION 1 // directly invoke MCGEN_EVENTSETINFORMATION(...). + #else // else + #define MCGEN_HAVE_EVENTSETINFORMATION 2 // find "EtwSetInformation" via MmGetSystemRoutineAddress. + #endif // else (using user-mode APIs) + #else // if target OS and SDK is Windows 8 or later, + #if WINVER >= 0x0602 && defined(EVENT_FILTER_TYPE_SCHEMATIZED) + #define MCGEN_HAVE_EVENTSETINFORMATION 1 // directly invoke MCGEN_EVENTSETINFORMATION(...). + #else // else + #define MCGEN_HAVE_EVENTSETINFORMATION 2 // find "EventSetInformation" via GetModuleHandleExW/GetProcAddress. + #endif + #endif +#endif // MCGEN_HAVE_EVENTSETINFORMATION + +// +// MCGEN Override Macros +// +// The following override macros may be defined before including this header +// to control the APIs used by this header: +// +// - MCGEN_EVENTREGISTER +// - MCGEN_EVENTUNREGISTER +// - MCGEN_EVENTSETINFORMATION +// - MCGEN_EVENTWRITETRANSFER +// +// If the the macro is undefined, the MC implementation will default to the +// corresponding ETW APIs. For example, if the MCGEN_EVENTREGISTER macro is +// undefined, the EventRegister[MyProviderName] macro will use EventRegister +// in user mode and will use EtwRegister in kernel mode. +// +// To prevent issues from conflicting definitions of these macros, the value +// of the override macro will be used as a suffix in certain internal function +// names. Because of this, the override macros must follow certain rules: +// +// - The macro must be defined before any MC-generated header is included and +// must not be undefined or redefined after any MC-generated header is +// included. Different translation units (i.e. different .c or .cpp files) +// may set the macros to different values, but within a translation unit +// (within a single .c or .cpp file), the macro must be set once and not +// changed. +// - The override must be an object-like macro, not a function-like macro +// (i.e. the override macro must not have a parameter list). +// - The override macro's value must be a simple identifier, i.e. must be +// something that starts with a letter or '_' and contains only letters, +// numbers, and '_' characters. +// - If the override macro's value is the name of a second object-like macro, +// the second object-like macro must follow the same rules. (The override +// macro's value can also be the name of a function-like macro, in which +// case the function-like macro does not need to follow the same rules.) +// +// For example, the following will cause compile errors: +// +// #define MCGEN_EVENTWRITETRANSFER MyNamespace::MyClass::MyFunction // Value has non-identifier characters (colon). +// #define MCGEN_EVENTWRITETRANSFER GetEventWriteFunctionPointer(7) // Value has non-identifier characters (parentheses). +// #define MCGEN_EVENTWRITETRANSFER(h,e,a,r,c,d) EventWrite(h,e,c,d) // Override is defined as a function-like macro. +// #define MY_OBJECT_LIKE_MACRO MyNamespace::MyClass::MyEventWriteFunction +// #define MCGEN_EVENTWRITETRANSFER MY_OBJECT_LIKE_MACRO // Evaluates to something with non-identifier characters (colon). +// +// The following would be ok: +// +// #define MCGEN_EVENTWRITETRANSFER MyEventWriteFunction1 // OK, suffix will be "MyEventWriteFunction1". +// #define MY_OBJECT_LIKE_MACRO MyEventWriteFunction2 +// #define MCGEN_EVENTWRITETRANSFER MY_OBJECT_LIKE_MACRO // OK, suffix will be "MyEventWriteFunction2". +// #define MY_FUNCTION_LIKE_MACRO(h,e,a,r,c,d) MyNamespace::MyClass::MyEventWriteFunction3(h,e,c,d) +// #define MCGEN_EVENTWRITETRANSFER MY_FUNCTION_LIKE_MACRO // OK, suffix will be "MY_FUNCTION_LIKE_MACRO". +// +#ifndef MCGEN_EVENTREGISTER + #if MCGEN_USE_KERNEL_MODE_APIS + #define MCGEN_EVENTREGISTER EtwRegister + #else + #define MCGEN_EVENTREGISTER EventRegister + #endif +#endif // MCGEN_EVENTREGISTER +#ifndef MCGEN_EVENTUNREGISTER + #if MCGEN_USE_KERNEL_MODE_APIS + #define MCGEN_EVENTUNREGISTER EtwUnregister + #else + #define MCGEN_EVENTUNREGISTER EventUnregister + #endif +#endif // MCGEN_EVENTUNREGISTER +#ifndef MCGEN_EVENTSETINFORMATION + #if MCGEN_USE_KERNEL_MODE_APIS + #define MCGEN_EVENTSETINFORMATION EtwSetInformation + #else + #define MCGEN_EVENTSETINFORMATION EventSetInformation + #endif +#endif // MCGEN_EVENTSETINFORMATION +#ifndef MCGEN_EVENTWRITETRANSFER + #if MCGEN_USE_KERNEL_MODE_APIS + #define MCGEN_EVENTWRITETRANSFER EtwWriteTransfer + #else + #define MCGEN_EVENTWRITETRANSFER EventWriteTransfer + #endif +#endif // MCGEN_EVENTWRITETRANSFER + +// +// MCGEN_EVENT_ENABLED macro: +// Override to control how the EventWrite[EventName] macros determine whether +// an event is enabled. The default behavior is for EventWrite[EventName] to +// use the EventEnabled[EventName] macros. +// +#ifndef MCGEN_EVENT_ENABLED +#define MCGEN_EVENT_ENABLED(EventName) EventEnabled##EventName() +#endif + +// +// MCGEN_EVENT_ENABLED_FORCONTEXT macro: +// Override to control how the EventWrite[EventName]_ForContext macros +// determine whether an event is enabled. The default behavior is for +// EventWrite[EventName]_ForContext to use the +// EventEnabled[EventName]_ForContext macros. +// +#ifndef MCGEN_EVENT_ENABLED_FORCONTEXT +#define MCGEN_EVENT_ENABLED_FORCONTEXT(pContext, EventName) EventEnabled##EventName##_ForContext(pContext) +#endif + +// +// MCGEN_ENABLE_CHECK macro: +// Determines whether the specified event would be considered as enabled +// based on the state of the specified context. Slightly faster than calling +// McGenEventEnabled directly. +// +#ifndef MCGEN_ENABLE_CHECK +#define MCGEN_ENABLE_CHECK(Context, Descriptor) (Context.IsEnabled && McGenEventEnabled(&Context, &Descriptor)) +#endif + +#if !defined(MCGEN_TRACE_CONTEXT_DEF) +#define MCGEN_TRACE_CONTEXT_DEF +// This structure is for use by MC-generated code and should not be used directly. +typedef struct _MCGEN_TRACE_CONTEXT +{ + TRACEHANDLE RegistrationHandle; + TRACEHANDLE Logger; // Used as pointer to provider traits. + ULONGLONG MatchAnyKeyword; + ULONGLONG MatchAllKeyword; + ULONG Flags; + ULONG IsEnabled; + UCHAR Level; + UCHAR Reserve; + USHORT EnableBitsCount; + PULONG EnableBitMask; + const ULONGLONG* EnableKeyWords; + const UCHAR* EnableLevel; +} MCGEN_TRACE_CONTEXT, *PMCGEN_TRACE_CONTEXT; +#endif // MCGEN_TRACE_CONTEXT_DEF + +#if !defined(MCGEN_LEVEL_KEYWORD_ENABLED_DEF) +#define MCGEN_LEVEL_KEYWORD_ENABLED_DEF +// +// Determines whether an event with a given Level and Keyword would be +// considered as enabled based on the state of the specified context. +// Note that you may want to use MCGEN_ENABLE_CHECK instead of calling this +// function directly. +// +FORCEINLINE +BOOLEAN +McGenLevelKeywordEnabled( + _In_ PMCGEN_TRACE_CONTEXT EnableInfo, + _In_ UCHAR Level, + _In_ ULONGLONG Keyword + ) +{ + // + // Check if the event Level is lower than the level at which + // the channel is enabled. + // If the event Level is 0 or the channel is enabled at level 0, + // all levels are enabled. + // + + if ((Level <= EnableInfo->Level) || // This also covers the case of Level == 0. + (EnableInfo->Level == 0)) { + + // + // Check if Keyword is enabled + // + + if ((Keyword == (ULONGLONG)0) || + ((Keyword & EnableInfo->MatchAnyKeyword) && + ((Keyword & EnableInfo->MatchAllKeyword) == EnableInfo->MatchAllKeyword))) { + return TRUE; + } + } + + return FALSE; +} +#endif // MCGEN_LEVEL_KEYWORD_ENABLED_DEF + +#if !defined(MCGEN_EVENT_ENABLED_DEF) +#define MCGEN_EVENT_ENABLED_DEF +// +// Determines whether the specified event would be considered as enabled based +// on the state of the specified context. Note that you may want to use +// MCGEN_ENABLE_CHECK instead of calling this function directly. +// +FORCEINLINE +BOOLEAN +McGenEventEnabled( + _In_ PMCGEN_TRACE_CONTEXT EnableInfo, + _In_ PCEVENT_DESCRIPTOR EventDescriptor + ) +{ + return McGenLevelKeywordEnabled(EnableInfo, EventDescriptor->Level, EventDescriptor->Keyword); +} +#endif // MCGEN_EVENT_ENABLED_DEF + +#if !defined(MCGEN_CONTROL_CALLBACK) +#define MCGEN_CONTROL_CALLBACK + +// This function is for use by MC-generated code and should not be used directly. +DECLSPEC_NOINLINE __inline +VOID +__stdcall +McGenControlCallbackV2( + _In_ LPCGUID SourceId, + _In_ ULONG ControlCode, + _In_ UCHAR Level, + _In_ ULONGLONG MatchAnyKeyword, + _In_ ULONGLONG MatchAllKeyword, + _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData, + _Inout_opt_ PVOID CallbackContext + ) +/*++ + +Routine Description: + + This is the notification callback for Windows Vista and later. + +Arguments: + + SourceId - The GUID that identifies the session that enabled the provider. + + ControlCode - The parameter indicates whether the provider + is being enabled or disabled. + + Level - The level at which the event is enabled. + + MatchAnyKeyword - The bitmask of keywords that the provider uses to + determine the category of events that it writes. + + MatchAllKeyword - This bitmask additionally restricts the category + of events that the provider writes. + + FilterData - The provider-defined data. + + CallbackContext - The context of the callback that is defined when the provider + called EtwRegister to register itself. + +Remarks: + + ETW calls this function to notify provider of enable/disable + +--*/ +{ + PMCGEN_TRACE_CONTEXT Ctx = (PMCGEN_TRACE_CONTEXT)CallbackContext; + ULONG Ix; +#ifndef MCGEN_PRIVATE_ENABLE_CALLBACK_V2 + UNREFERENCED_PARAMETER(SourceId); + UNREFERENCED_PARAMETER(FilterData); +#endif + + if (Ctx == NULL) { + return; + } + + switch (ControlCode) { + + case EVENT_CONTROL_CODE_ENABLE_PROVIDER: + Ctx->Level = Level; + Ctx->MatchAnyKeyword = MatchAnyKeyword; + Ctx->MatchAllKeyword = MatchAllKeyword; + Ctx->IsEnabled = EVENT_CONTROL_CODE_ENABLE_PROVIDER; + + for (Ix = 0; Ix < Ctx->EnableBitsCount; Ix += 1) { + if (McGenLevelKeywordEnabled(Ctx, Ctx->EnableLevel[Ix], Ctx->EnableKeyWords[Ix]) != FALSE) { + Ctx->EnableBitMask[Ix >> 5] |= (1 << (Ix % 32)); + } else { + Ctx->EnableBitMask[Ix >> 5] &= ~(1 << (Ix % 32)); + } + } + break; + + case EVENT_CONTROL_CODE_DISABLE_PROVIDER: + Ctx->IsEnabled = EVENT_CONTROL_CODE_DISABLE_PROVIDER; + Ctx->Level = 0; + Ctx->MatchAnyKeyword = 0; + Ctx->MatchAllKeyword = 0; + if (Ctx->EnableBitsCount > 0) { +#pragma warning(suppress: 26451) // Arithmetic overflow cannot occur, no matter the value of EnableBitCount + RtlZeroMemory(Ctx->EnableBitMask, (((Ctx->EnableBitsCount - 1) / 32) + 1) * sizeof(ULONG)); + } + break; + + default: + break; + } + +#ifdef MCGEN_PRIVATE_ENABLE_CALLBACK_V2 + // + // Call user defined callback + // + MCGEN_PRIVATE_ENABLE_CALLBACK_V2( + SourceId, + ControlCode, + Level, + MatchAnyKeyword, + MatchAllKeyword, + FilterData, + CallbackContext + ); +#endif // MCGEN_PRIVATE_ENABLE_CALLBACK_V2 + + return; +} + +#endif // MCGEN_CONTROL_CALLBACK + +#ifndef _mcgen_PENABLECALLBACK + #if MCGEN_USE_KERNEL_MODE_APIS + #define _mcgen_PENABLECALLBACK PETWENABLECALLBACK + #else + #define _mcgen_PENABLECALLBACK PENABLECALLBACK + #endif +#endif // _mcgen_PENABLECALLBACK + +#if !defined(_mcgen_PASTE2) +// This macro is for use by MC-generated code and should not be used directly. +#define _mcgen_PASTE2(a, b) _mcgen_PASTE2_imp(a, b) +#define _mcgen_PASTE2_imp(a, b) a##b +#endif // _mcgen_PASTE2 + +#if !defined(_mcgen_PASTE3) +// This macro is for use by MC-generated code and should not be used directly. +#define _mcgen_PASTE3(a, b, c) _mcgen_PASTE3_imp(a, b, c) +#define _mcgen_PASTE3_imp(a, b, c) a##b##_##c +#endif // _mcgen_PASTE3 + +// +// Macro validation +// + +// Validate MCGEN_EVENTREGISTER: + +// Trigger an error if MCGEN_EVENTREGISTER is not an unqualified (simple) identifier: +struct _mcgen_PASTE2(MCGEN_EVENTREGISTER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTREGISTER); + +// Trigger an error if MCGEN_EVENTREGISTER is redefined: +typedef struct _mcgen_PASTE2(MCGEN_EVENTREGISTER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTREGISTER) + MCGEN_EVENTREGISTER_must_not_be_redefined_between_headers; + +// Trigger an error if MCGEN_EVENTREGISTER is defined as a function-like macro: +typedef void MCGEN_EVENTREGISTER_must_not_be_a_functionLike_macro_MCGEN_EVENTREGISTER; +typedef int _mcgen_PASTE2(MCGEN_EVENTREGISTER_must_not_be_a_functionLike_macro_, MCGEN_EVENTREGISTER); + +// Validate MCGEN_EVENTUNREGISTER: + +// Trigger an error if MCGEN_EVENTUNREGISTER is not an unqualified (simple) identifier: +struct _mcgen_PASTE2(MCGEN_EVENTUNREGISTER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTUNREGISTER); + +// Trigger an error if MCGEN_EVENTUNREGISTER is redefined: +typedef struct _mcgen_PASTE2(MCGEN_EVENTUNREGISTER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTUNREGISTER) + MCGEN_EVENTUNREGISTER_must_not_be_redefined_between_headers; + +// Trigger an error if MCGEN_EVENTUNREGISTER is defined as a function-like macro: +typedef void MCGEN_EVENTUNREGISTER_must_not_be_a_functionLike_macro_MCGEN_EVENTUNREGISTER; +typedef int _mcgen_PASTE2(MCGEN_EVENTUNREGISTER_must_not_be_a_functionLike_macro_, MCGEN_EVENTUNREGISTER); + +// Validate MCGEN_EVENTSETINFORMATION: + +// Trigger an error if MCGEN_EVENTSETINFORMATION is not an unqualified (simple) identifier: +struct _mcgen_PASTE2(MCGEN_EVENTSETINFORMATION_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTSETINFORMATION); + +// Trigger an error if MCGEN_EVENTSETINFORMATION is redefined: +typedef struct _mcgen_PASTE2(MCGEN_EVENTSETINFORMATION_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTSETINFORMATION) + MCGEN_EVENTSETINFORMATION_must_not_be_redefined_between_headers; + +// Trigger an error if MCGEN_EVENTSETINFORMATION is defined as a function-like macro: +typedef void MCGEN_EVENTSETINFORMATION_must_not_be_a_functionLike_macro_MCGEN_EVENTSETINFORMATION; +typedef int _mcgen_PASTE2(MCGEN_EVENTSETINFORMATION_must_not_be_a_functionLike_macro_, MCGEN_EVENTSETINFORMATION); + +// Validate MCGEN_EVENTWRITETRANSFER: + +// Trigger an error if MCGEN_EVENTWRITETRANSFER is not an unqualified (simple) identifier: +struct _mcgen_PASTE2(MCGEN_EVENTWRITETRANSFER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTWRITETRANSFER); + +// Trigger an error if MCGEN_EVENTWRITETRANSFER is redefined: +typedef struct _mcgen_PASTE2(MCGEN_EVENTWRITETRANSFER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTWRITETRANSFER) + MCGEN_EVENTWRITETRANSFER_must_not_be_redefined_between_headers;; + +// Trigger an error if MCGEN_EVENTWRITETRANSFER is defined as a function-like macro: +typedef void MCGEN_EVENTWRITETRANSFER_must_not_be_a_functionLike_macro_MCGEN_EVENTWRITETRANSFER; +typedef int _mcgen_PASTE2(MCGEN_EVENTWRITETRANSFER_must_not_be_a_functionLike_macro_, MCGEN_EVENTWRITETRANSFER); + +#ifndef McGenEventWrite_def +#define McGenEventWrite_def + +// This macro is for use by MC-generated code and should not be used directly. +#define McGenEventWrite _mcgen_PASTE2(McGenEventWrite_, MCGEN_EVENTWRITETRANSFER) + +// This function is for use by MC-generated code and should not be used directly. +DECLSPEC_NOINLINE __inline +ULONG __stdcall +McGenEventWrite( + _In_ PMCGEN_TRACE_CONTEXT Context, + _In_ PCEVENT_DESCRIPTOR Descriptor, + _In_opt_ LPCGUID ActivityId, + _In_range_(1, 128) ULONG EventDataCount, + _Pre_cap_(EventDataCount) EVENT_DATA_DESCRIPTOR* EventData + ) +{ + const USHORT UNALIGNED* Traits; + + // Some customized MCGEN_EVENTWRITETRANSFER macros might ignore ActivityId. + UNREFERENCED_PARAMETER(ActivityId); + + Traits = (const USHORT UNALIGNED*)(UINT_PTR)Context->Logger; + + if (Traits == NULL) { + EventData[0].Ptr = 0; + EventData[0].Size = 0; + EventData[0].Reserved = 0; + } else { + EventData[0].Ptr = (ULONG_PTR)Traits; + EventData[0].Size = *Traits; + EventData[0].Reserved = 2; // EVENT_DATA_DESCRIPTOR_TYPE_PROVIDER_METADATA + } + + return MCGEN_EVENTWRITETRANSFER( + Context->RegistrationHandle, + Descriptor, + ActivityId, + NULL, + EventDataCount, + EventData); +} +#endif // McGenEventWrite_def + +#if !defined(McGenEventRegisterUnregister) +#define McGenEventRegisterUnregister + +// This macro is for use by MC-generated code and should not be used directly. +#define McGenEventRegister _mcgen_PASTE2(McGenEventRegister_, MCGEN_EVENTREGISTER) + +#pragma warning(push) +#pragma warning(disable:6103) +// This function is for use by MC-generated code and should not be used directly. +DECLSPEC_NOINLINE __inline +ULONG __stdcall +McGenEventRegister( + _In_ LPCGUID ProviderId, + _In_opt_ _mcgen_PENABLECALLBACK EnableCallback, + _In_opt_ PVOID CallbackContext, + _Inout_ PREGHANDLE RegHandle + ) +/*++ + +Routine Description: + + This function registers the provider with ETW. + +Arguments: + + ProviderId - Provider ID to register with ETW. + + EnableCallback - Callback to be used. + + CallbackContext - Context for the callback. + + RegHandle - Pointer to registration handle. + +Remarks: + + Should not be called if the provider is already registered (i.e. should not + be called if *RegHandle != 0). Repeatedly registering a provider is a bug + and may indicate a race condition. However, for compatibility with previous + behavior, this function will return SUCCESS in this case. + +--*/ +{ + ULONG Error; + + if (*RegHandle != 0) + { + Error = 0; // ERROR_SUCCESS + } + else + { + Error = MCGEN_EVENTREGISTER(ProviderId, EnableCallback, CallbackContext, RegHandle); + } + + return Error; +} +#pragma warning(pop) + +// This macro is for use by MC-generated code and should not be used directly. +#define McGenEventUnregister _mcgen_PASTE2(McGenEventUnregister_, MCGEN_EVENTUNREGISTER) + +// This function is for use by MC-generated code and should not be used directly. +DECLSPEC_NOINLINE __inline +ULONG __stdcall +McGenEventUnregister(_Inout_ PREGHANDLE RegHandle) +/*++ + +Routine Description: + + Unregister from ETW and set *RegHandle = 0. + +Arguments: + + RegHandle - the pointer to the provider registration handle + +Remarks: + + If provider has not been registered (i.e. if *RegHandle == 0), + return SUCCESS. It is safe to call McGenEventUnregister even if the + call to McGenEventRegister returned an error. + +--*/ +{ + ULONG Error; + + if(*RegHandle == 0) + { + Error = 0; // ERROR_SUCCESS + } + else + { + Error = MCGEN_EVENTUNREGISTER(*RegHandle); + *RegHandle = (REGHANDLE)0; + } + + return Error; +} + +#endif // McGenEventRegisterUnregister + +#ifndef _mcgen_EVENT_BIT_SET + #if defined(_M_IX86) || defined(_M_X64) + // This macro is for use by MC-generated code and should not be used directly. + #define _mcgen_EVENT_BIT_SET(EnableBits, BitPosition) ((((const unsigned char*)EnableBits)[BitPosition >> 3] & (1u << (BitPosition & 7))) != 0) + #else // CPU type + // This macro is for use by MC-generated code and should not be used directly. + #define _mcgen_EVENT_BIT_SET(EnableBits, BitPosition) ((EnableBits[BitPosition >> 5] & (1u << (BitPosition & 31))) != 0) + #endif // CPU type +#endif // _mcgen_EVENT_BIT_SET + +#endif // MCGEN_DISABLE_PROVIDER_CODE_GENERATION + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// Provider "microsoft-windows-mimalloc" event count 2 +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +// Provider GUID = 138f4dbb-ee04-4899-aa0a-572ad4475779 +EXTERN_C __declspec(selectany) const GUID ETW_MI_Provider = {0x138f4dbb, 0xee04, 0x4899, {0xaa, 0x0a, 0x57, 0x2a, 0xd4, 0x47, 0x57, 0x79}}; + +#ifndef ETW_MI_Provider_Traits +#define ETW_MI_Provider_Traits NULL +#endif // ETW_MI_Provider_Traits + +// +// Event Descriptors +// +EXTERN_C __declspec(selectany) const EVENT_DESCRIPTOR ETW_MI_ALLOC = {0x64, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0}; +#define ETW_MI_ALLOC_value 0x64 +EXTERN_C __declspec(selectany) const EVENT_DESCRIPTOR ETW_MI_FREE = {0x65, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0}; +#define ETW_MI_FREE_value 0x65 + +// +// MCGEN_DISABLE_PROVIDER_CODE_GENERATION macro: +// Define this macro to have the compiler skip the generated functions in this +// header. +// +#ifndef MCGEN_DISABLE_PROVIDER_CODE_GENERATION + +// +// Event Enablement Bits +// These variables are for use by MC-generated code and should not be used directly. +// +EXTERN_C __declspec(selectany) DECLSPEC_CACHEALIGN ULONG microsoft_windows_mimallocEnableBits[1]; +EXTERN_C __declspec(selectany) const ULONGLONG microsoft_windows_mimallocKeywords[1] = {0x0}; +EXTERN_C __declspec(selectany) const unsigned char microsoft_windows_mimallocLevels[1] = {4}; + +// +// Provider context +// +EXTERN_C __declspec(selectany) MCGEN_TRACE_CONTEXT ETW_MI_Provider_Context = {0, (ULONG_PTR)ETW_MI_Provider_Traits, 0, 0, 0, 0, 0, 0, 1, microsoft_windows_mimallocEnableBits, microsoft_windows_mimallocKeywords, microsoft_windows_mimallocLevels}; + +// +// Provider REGHANDLE +// +#define microsoft_windows_mimallocHandle (ETW_MI_Provider_Context.RegistrationHandle) + +// +// This macro is set to 0, indicating that the EventWrite[Name] macros do not +// have an Activity parameter. This is controlled by the -km and -um options. +// +#define ETW_MI_Provider_EventWriteActivity 0 + +// +// Register with ETW using the control GUID specified in the manifest. +// Invoke this macro during module initialization (i.e. program startup, +// DLL process attach, or driver load) to initialize the provider. +// Note that if this function returns an error, the error means that +// will not work, but no action needs to be taken -- even if EventRegister +// returns an error, it is generally safe to use EventWrite and +// EventUnregister macros (they will be no-ops if EventRegister failed). +// +#ifndef EventRegistermicrosoft_windows_mimalloc +#define EventRegistermicrosoft_windows_mimalloc() McGenEventRegister(&ETW_MI_Provider, McGenControlCallbackV2, &ETW_MI_Provider_Context, µsoft_windows_mimallocHandle) +#endif + +// +// Register with ETW using a specific control GUID (i.e. a GUID other than what +// is specified in the manifest). Advanced scenarios only. +// +#ifndef EventRegisterByGuidmicrosoft_windows_mimalloc +#define EventRegisterByGuidmicrosoft_windows_mimalloc(Guid) McGenEventRegister(&(Guid), McGenControlCallbackV2, &ETW_MI_Provider_Context, µsoft_windows_mimallocHandle) +#endif + +// +// Unregister with ETW and close the provider. +// Invoke this macro during module shutdown (i.e. program exit, DLL process +// detach, or driver unload) to unregister the provider. +// Note that you MUST call EventUnregister before DLL or driver unload +// (not optional): failure to unregister a provider before DLL or driver unload +// will result in crashes. +// +#ifndef EventUnregistermicrosoft_windows_mimalloc +#define EventUnregistermicrosoft_windows_mimalloc() McGenEventUnregister(µsoft_windows_mimallocHandle) +#endif + +// +// MCGEN_ENABLE_FORCONTEXT_CODE_GENERATION macro: +// Define this macro to enable support for caller-allocated provider context. +// +#ifdef MCGEN_ENABLE_FORCONTEXT_CODE_GENERATION + +// +// Advanced scenarios: Caller-allocated provider context. +// Use when multiple differently-configured provider handles are needed, +// e.g. for container-aware drivers, one context per container. +// +// Usage: +// +// - Caller enables the feature before including this header, e.g. +// #define MCGEN_ENABLE_FORCONTEXT_CODE_GENERATION 1 +// - Caller allocates memory, e.g. pContext = malloc(sizeof(McGenContext_microsoft_windows_mimalloc)); +// - Caller registers the provider, e.g. EventRegistermicrosoft_windows_mimalloc_ForContext(pContext); +// - Caller writes events, e.g. EventWriteMyEvent_ForContext(pContext, ...); +// - Caller unregisters, e.g. EventUnregistermicrosoft_windows_mimalloc_ForContext(pContext); +// - Caller frees memory, e.g. free(pContext); +// + +typedef struct tagMcGenContext_microsoft_windows_mimalloc { + // The fields of this structure are subject to change and should + // not be accessed directly. To access the provider's REGHANDLE, + // use microsoft_windows_mimallocHandle_ForContext(pContext). + MCGEN_TRACE_CONTEXT Context; + ULONG EnableBits[1]; +} McGenContext_microsoft_windows_mimalloc; + +#define EventRegistermicrosoft_windows_mimalloc_ForContext(pContext) _mcgen_PASTE2(_mcgen_RegisterForContext_microsoft_windows_mimalloc_, MCGEN_EVENTREGISTER)(&ETW_MI_Provider, pContext) +#define EventRegisterByGuidmicrosoft_windows_mimalloc_ForContext(Guid, pContext) _mcgen_PASTE2(_mcgen_RegisterForContext_microsoft_windows_mimalloc_, MCGEN_EVENTREGISTER)(&(Guid), pContext) +#define EventUnregistermicrosoft_windows_mimalloc_ForContext(pContext) McGenEventUnregister(&(pContext)->Context.RegistrationHandle) + +// +// Provider REGHANDLE for caller-allocated context. +// +#define microsoft_windows_mimallocHandle_ForContext(pContext) ((pContext)->Context.RegistrationHandle) + +// This function is for use by MC-generated code and should not be used directly. +// Initialize and register the caller-allocated context. +__inline +ULONG __stdcall +_mcgen_PASTE2(_mcgen_RegisterForContext_microsoft_windows_mimalloc_, MCGEN_EVENTREGISTER)( + _In_ LPCGUID pProviderId, + _Out_ McGenContext_microsoft_windows_mimalloc* pContext) +{ + RtlZeroMemory(pContext, sizeof(*pContext)); + pContext->Context.Logger = (ULONG_PTR)ETW_MI_Provider_Traits; + pContext->Context.EnableBitsCount = 1; + pContext->Context.EnableBitMask = pContext->EnableBits; + pContext->Context.EnableKeyWords = microsoft_windows_mimallocKeywords; + pContext->Context.EnableLevel = microsoft_windows_mimallocLevels; + return McGenEventRegister( + pProviderId, + McGenControlCallbackV2, + &pContext->Context, + &pContext->Context.RegistrationHandle); +} + +// This function is for use by MC-generated code and should not be used directly. +// Trigger a compile error if called with the wrong parameter type. +FORCEINLINE +_Ret_ McGenContext_microsoft_windows_mimalloc* +_mcgen_CheckContextType_microsoft_windows_mimalloc(_In_ McGenContext_microsoft_windows_mimalloc* pContext) +{ + return pContext; +} + +#endif // MCGEN_ENABLE_FORCONTEXT_CODE_GENERATION + +// +// Enablement check macro for event "ETW_MI_ALLOC" +// +#define EventEnabledETW_MI_ALLOC() _mcgen_EVENT_BIT_SET(microsoft_windows_mimallocEnableBits, 0) +#define EventEnabledETW_MI_ALLOC_ForContext(pContext) _mcgen_EVENT_BIT_SET(_mcgen_CheckContextType_microsoft_windows_mimalloc(pContext)->EnableBits, 0) + +// +// Event write macros for event "ETW_MI_ALLOC" +// +#define EventWriteETW_MI_ALLOC(Address, Size) \ + MCGEN_EVENT_ENABLED(ETW_MI_ALLOC) \ + ? _mcgen_TEMPLATE_FOR_ETW_MI_ALLOC(&ETW_MI_Provider_Context, &ETW_MI_ALLOC, Address, Size) : 0 +#define EventWriteETW_MI_ALLOC_AssumeEnabled(Address, Size) \ + _mcgen_TEMPLATE_FOR_ETW_MI_ALLOC(&ETW_MI_Provider_Context, &ETW_MI_ALLOC, Address, Size) +#define EventWriteETW_MI_ALLOC_ForContext(pContext, Address, Size) \ + MCGEN_EVENT_ENABLED_FORCONTEXT(pContext, ETW_MI_ALLOC) \ + ? _mcgen_TEMPLATE_FOR_ETW_MI_ALLOC(&(pContext)->Context, &ETW_MI_ALLOC, Address, Size) : 0 +#define EventWriteETW_MI_ALLOC_ForContextAssumeEnabled(pContext, Address, Size) \ + _mcgen_TEMPLATE_FOR_ETW_MI_ALLOC(&_mcgen_CheckContextType_microsoft_windows_mimalloc(pContext)->Context, &ETW_MI_ALLOC, Address, Size) + +// This macro is for use by MC-generated code and should not be used directly. +#define _mcgen_TEMPLATE_FOR_ETW_MI_ALLOC _mcgen_PASTE2(McTemplateU0xx_, MCGEN_EVENTWRITETRANSFER) + +// +// Enablement check macro for event "ETW_MI_FREE" +// +#define EventEnabledETW_MI_FREE() _mcgen_EVENT_BIT_SET(microsoft_windows_mimallocEnableBits, 0) +#define EventEnabledETW_MI_FREE_ForContext(pContext) _mcgen_EVENT_BIT_SET(_mcgen_CheckContextType_microsoft_windows_mimalloc(pContext)->EnableBits, 0) + +// +// Event write macros for event "ETW_MI_FREE" +// +#define EventWriteETW_MI_FREE(Address, Size) \ + MCGEN_EVENT_ENABLED(ETW_MI_FREE) \ + ? _mcgen_TEMPLATE_FOR_ETW_MI_FREE(&ETW_MI_Provider_Context, &ETW_MI_FREE, Address, Size) : 0 +#define EventWriteETW_MI_FREE_AssumeEnabled(Address, Size) \ + _mcgen_TEMPLATE_FOR_ETW_MI_FREE(&ETW_MI_Provider_Context, &ETW_MI_FREE, Address, Size) +#define EventWriteETW_MI_FREE_ForContext(pContext, Address, Size) \ + MCGEN_EVENT_ENABLED_FORCONTEXT(pContext, ETW_MI_FREE) \ + ? _mcgen_TEMPLATE_FOR_ETW_MI_FREE(&(pContext)->Context, &ETW_MI_FREE, Address, Size) : 0 +#define EventWriteETW_MI_FREE_ForContextAssumeEnabled(pContext, Address, Size) \ + _mcgen_TEMPLATE_FOR_ETW_MI_FREE(&_mcgen_CheckContextType_microsoft_windows_mimalloc(pContext)->Context, &ETW_MI_FREE, Address, Size) + +// This macro is for use by MC-generated code and should not be used directly. +#define _mcgen_TEMPLATE_FOR_ETW_MI_FREE _mcgen_PASTE2(McTemplateU0xx_, MCGEN_EVENTWRITETRANSFER) + +#endif // MCGEN_DISABLE_PROVIDER_CODE_GENERATION + +// +// MCGEN_DISABLE_PROVIDER_CODE_GENERATION macro: +// Define this macro to have the compiler skip the generated functions in this +// header. +// +#ifndef MCGEN_DISABLE_PROVIDER_CODE_GENERATION + +// +// Template Functions +// + +// +// Function for template "ETW_CUSTOM_HEAP_ALLOC_DATA" (and possibly others). +// This function is for use by MC-generated code and should not be used directly. +// +#ifndef McTemplateU0xx_def +#define McTemplateU0xx_def +ETW_INLINE +ULONG +_mcgen_PASTE2(McTemplateU0xx_, MCGEN_EVENTWRITETRANSFER)( + _In_ PMCGEN_TRACE_CONTEXT Context, + _In_ PCEVENT_DESCRIPTOR Descriptor, + _In_ const unsigned __int64 _Arg0, + _In_ const unsigned __int64 _Arg1 + ) +{ +#define McTemplateU0xx_ARGCOUNT 2 + + EVENT_DATA_DESCRIPTOR EventData[McTemplateU0xx_ARGCOUNT + 1]; + + EventDataDescCreate(&EventData[1],&_Arg0, sizeof(const unsigned __int64) ); + + EventDataDescCreate(&EventData[2],&_Arg1, sizeof(const unsigned __int64) ); + + return McGenEventWrite(Context, Descriptor, NULL, McTemplateU0xx_ARGCOUNT + 1, EventData); +} +#endif // McTemplateU0xx_def + +#endif // MCGEN_DISABLE_PROVIDER_CODE_GENERATION + +#if defined(__cplusplus) +} +#endif diff --git a/Source/mimalloc/src/prim/windows/etw.man b/Source/mimalloc/src/prim/windows/etw.man new file mode 100644 index 0000000000000000000000000000000000000000..cfd1f8a9eaacd50af63f1e28f9540aa88c20f90c GIT binary patch literal 3926 zcmeH~T~8B16o${WiT`2c+NGc<*i;EYh$d8xl<0*CS-Mb~vPP(R>hsQY*nU!q z$b})3?##}d?{nTW+uy%xwr*doYaNU1!Vc}sa%7F%g+hVAmL$hwL?4dodnmuAKhUXm0)X9|WHj>XRahh@~SWDIkbduX;B#u6^Q>@U= zDO8U+KXa0*th&%f*z^Udh4ol@uE=Q&`emUsh_CA`FOXgI{i-`XZ9C#bR1yBm=PJ*p z9kVN$J6O;h;8HY>p)RnhY8A#Hb?z)_!y(Iaen(I)@-9CrSSp(;_Jn9I*$S&ATjP1? zVxB>pV@LVsy;^jZr7r$HNAm06TcUiI`l@~F$Mt$E%Shfd3O+h1vFhR9a8yQZ@wpne zr3bI-p=VEdo{)#uWxSVJeYQF|-5tnq>~f+CP~A0&{v=(up=ngEDl>5!$EDwPRaNjW zXj|wbG$OwmwaW-hMvBK%pbm3wpic71O^dzbxT%*13+SP9h- zI~rA5hapTVnk|qmiIVYy{__+x9f7OV4j3`g4;{{8_SWnLBSu2PUc%~`t%Ae^>J`SS zdtZg-r<0xAH*_ALtK;Nv(d9nbKK1jK=Ld)I(jQrKhBjgToR#Wm8{0a}@6ZuEO(lvG=y=Jh{M!4!-$(E)!vYUrf47 znf4W$e&QFOovV_$>J%X*KN>oXI@jt%BJms>IU}I$<7Hr{PChchs%+mx{% zt(fa_PM4r63?1h#YOk~;byc5`>%q>sLHA1gohNq{qOREhxu<y~`}YVhGe0?R_ceQ8vt^BpSNp6kErj_0hUNFyWQ1IOZ|GEgWBPwYFLgFu Oo!*uqEBu%Ae18C_t1cS= literal 0 HcmV?d00001 diff --git a/Source/mimalloc/src/prim/windows/prim.c b/Source/mimalloc/src/prim/windows/prim.c new file mode 100644 index 000000000..e3dc33e32 --- /dev/null +++ b/Source/mimalloc/src/prim/windows/prim.c @@ -0,0 +1,607 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +// This file is included in `src/prim/prim.c` + +#include "mimalloc.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" +#include "mimalloc/prim.h" +#include // fputs, stderr + + +//--------------------------------------------- +// Dynamically bind Windows API points for portability +//--------------------------------------------- + +// We use VirtualAlloc2 for aligned allocation, but it is only supported on Windows 10 and Windows Server 2016. +// So, we need to look it up dynamically to run on older systems. (use __stdcall for 32-bit compatibility) +// NtAllocateVirtualAllocEx is used for huge OS page allocation (1GiB) +// We define a minimal MEM_EXTENDED_PARAMETER ourselves in order to be able to compile with older SDK's. +typedef enum MI_MEM_EXTENDED_PARAMETER_TYPE_E { + MiMemExtendedParameterInvalidType = 0, + MiMemExtendedParameterAddressRequirements, + MiMemExtendedParameterNumaNode, + MiMemExtendedParameterPartitionHandle, + MiMemExtendedParameterUserPhysicalHandle, + MiMemExtendedParameterAttributeFlags, + MiMemExtendedParameterMax +} MI_MEM_EXTENDED_PARAMETER_TYPE; + +typedef struct DECLSPEC_ALIGN(8) MI_MEM_EXTENDED_PARAMETER_S { + struct { DWORD64 Type : 8; DWORD64 Reserved : 56; } Type; + union { DWORD64 ULong64; PVOID Pointer; SIZE_T Size; HANDLE Handle; DWORD ULong; } Arg; +} MI_MEM_EXTENDED_PARAMETER; + +typedef struct MI_MEM_ADDRESS_REQUIREMENTS_S { + PVOID LowestStartingAddress; + PVOID HighestEndingAddress; + SIZE_T Alignment; +} MI_MEM_ADDRESS_REQUIREMENTS; + +#define MI_MEM_EXTENDED_PARAMETER_NONPAGED_HUGE 0x00000010 + +#include +typedef PVOID (__stdcall *PVirtualAlloc2)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MI_MEM_EXTENDED_PARAMETER*, ULONG); +typedef NTSTATUS (__stdcall *PNtAllocateVirtualMemoryEx)(HANDLE, PVOID*, SIZE_T*, ULONG, ULONG, MI_MEM_EXTENDED_PARAMETER*, ULONG); +static PVirtualAlloc2 pVirtualAlloc2 = NULL; +static PNtAllocateVirtualMemoryEx pNtAllocateVirtualMemoryEx = NULL; + +// Similarly, GetNumaProcesorNodeEx is only supported since Windows 7 +typedef struct MI_PROCESSOR_NUMBER_S { WORD Group; BYTE Number; BYTE Reserved; } MI_PROCESSOR_NUMBER; + +typedef VOID (__stdcall *PGetCurrentProcessorNumberEx)(MI_PROCESSOR_NUMBER* ProcNumber); +typedef BOOL (__stdcall *PGetNumaProcessorNodeEx)(MI_PROCESSOR_NUMBER* Processor, PUSHORT NodeNumber); +typedef BOOL (__stdcall* PGetNumaNodeProcessorMaskEx)(USHORT Node, PGROUP_AFFINITY ProcessorMask); +typedef BOOL (__stdcall *PGetNumaProcessorNode)(UCHAR Processor, PUCHAR NodeNumber); +static PGetCurrentProcessorNumberEx pGetCurrentProcessorNumberEx = NULL; +static PGetNumaProcessorNodeEx pGetNumaProcessorNodeEx = NULL; +static PGetNumaNodeProcessorMaskEx pGetNumaNodeProcessorMaskEx = NULL; +static PGetNumaProcessorNode pGetNumaProcessorNode = NULL; + +//--------------------------------------------- +// Enable large page support dynamically (if possible) +//--------------------------------------------- + +static bool win_enable_large_os_pages(size_t* large_page_size) +{ + static bool large_initialized = false; + if (large_initialized) return (_mi_os_large_page_size() > 0); + large_initialized = true; + + // Try to see if large OS pages are supported + // To use large pages on Windows, we first need access permission + // Set "Lock pages in memory" permission in the group policy editor + // + unsigned long err = 0; + HANDLE token = NULL; + BOOL ok = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token); + if (ok) { + TOKEN_PRIVILEGES tp; + ok = LookupPrivilegeValue(NULL, TEXT("SeLockMemoryPrivilege"), &tp.Privileges[0].Luid); + if (ok) { + tp.PrivilegeCount = 1; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + ok = AdjustTokenPrivileges(token, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, 0); + if (ok) { + err = GetLastError(); + ok = (err == ERROR_SUCCESS); + if (ok && large_page_size != NULL) { + *large_page_size = GetLargePageMinimum(); + } + } + } + CloseHandle(token); + } + if (!ok) { + if (err == 0) err = GetLastError(); + _mi_warning_message("cannot enable large OS page support, error %lu\n", err); + } + return (ok!=0); +} + + +//--------------------------------------------- +// Initialize +//--------------------------------------------- + +void _mi_prim_mem_init( mi_os_mem_config_t* config ) +{ + config->has_overcommit = false; + config->must_free_whole = true; + // get the page size + SYSTEM_INFO si; + GetSystemInfo(&si); + if (si.dwPageSize > 0) { config->page_size = si.dwPageSize; } + if (si.dwAllocationGranularity > 0) { config->alloc_granularity = si.dwAllocationGranularity; } + // get the VirtualAlloc2 function + HINSTANCE hDll; + hDll = LoadLibrary(TEXT("kernelbase.dll")); + if (hDll != NULL) { + // use VirtualAlloc2FromApp if possible as it is available to Windows store apps + pVirtualAlloc2 = (PVirtualAlloc2)(void (*)(void))GetProcAddress(hDll, "VirtualAlloc2FromApp"); + if (pVirtualAlloc2==NULL) pVirtualAlloc2 = (PVirtualAlloc2)(void (*)(void))GetProcAddress(hDll, "VirtualAlloc2"); + FreeLibrary(hDll); + } + // NtAllocateVirtualMemoryEx is used for huge page allocation + hDll = LoadLibrary(TEXT("ntdll.dll")); + if (hDll != NULL) { + pNtAllocateVirtualMemoryEx = (PNtAllocateVirtualMemoryEx)(void (*)(void))GetProcAddress(hDll, "NtAllocateVirtualMemoryEx"); + FreeLibrary(hDll); + } + // Try to use Win7+ numa API + hDll = LoadLibrary(TEXT("kernel32.dll")); + if (hDll != NULL) { + pGetCurrentProcessorNumberEx = (PGetCurrentProcessorNumberEx)(void (*)(void))GetProcAddress(hDll, "GetCurrentProcessorNumberEx"); + pGetNumaProcessorNodeEx = (PGetNumaProcessorNodeEx)(void (*)(void))GetProcAddress(hDll, "GetNumaProcessorNodeEx"); + pGetNumaNodeProcessorMaskEx = (PGetNumaNodeProcessorMaskEx)(void (*)(void))GetProcAddress(hDll, "GetNumaNodeProcessorMaskEx"); + pGetNumaProcessorNode = (PGetNumaProcessorNode)(void (*)(void))GetProcAddress(hDll, "GetNumaProcessorNode"); + FreeLibrary(hDll); + } + if (mi_option_is_enabled(mi_option_large_os_pages) || mi_option_is_enabled(mi_option_reserve_huge_os_pages)) { + win_enable_large_os_pages(&config->large_page_size); + } +} + + +//--------------------------------------------- +// Free +//--------------------------------------------- + +int _mi_prim_free(void* addr, size_t size ) { + MI_UNUSED(size); + DWORD errcode = 0; + bool err = (VirtualFree(addr, 0, MEM_RELEASE) == 0); + if (err) { errcode = GetLastError(); } + if (errcode == ERROR_INVALID_ADDRESS) { + // In mi_os_mem_alloc_aligned the fallback path may have returned a pointer inside + // the memory region returned by VirtualAlloc; in that case we need to free using + // the start of the region. + MEMORY_BASIC_INFORMATION info = { 0 }; + VirtualQuery(addr, &info, sizeof(info)); + if (info.AllocationBase < addr && ((uint8_t*)addr - (uint8_t*)info.AllocationBase) < (ptrdiff_t)MI_SEGMENT_SIZE) { + errcode = 0; + err = (VirtualFree(info.AllocationBase, 0, MEM_RELEASE) == 0); + if (err) { errcode = GetLastError(); } + } + } + return (int)errcode; +} + + +//--------------------------------------------- +// VirtualAlloc +//--------------------------------------------- + +static void* win_virtual_alloc_prim(void* addr, size_t size, size_t try_alignment, DWORD flags) { + #if (MI_INTPTR_SIZE >= 8) + // on 64-bit systems, try to use the virtual address area after 2TiB for 4MiB aligned allocations + if (addr == NULL) { + void* hint = _mi_os_get_aligned_hint(try_alignment,size); + if (hint != NULL) { + void* p = VirtualAlloc(hint, size, flags, PAGE_READWRITE); + if (p != NULL) return p; + _mi_verbose_message("warning: unable to allocate hinted aligned OS memory (%zu bytes, error code: 0x%x, address: %p, alignment: %zu, flags: 0x%x)\n", size, GetLastError(), hint, try_alignment, flags); + // fall through on error + } + } + #endif + // on modern Windows try use VirtualAlloc2 for aligned allocation + if (try_alignment > 1 && (try_alignment % _mi_os_page_size()) == 0 && pVirtualAlloc2 != NULL) { + MI_MEM_ADDRESS_REQUIREMENTS reqs = { 0, 0, 0 }; + reqs.Alignment = try_alignment; + MI_MEM_EXTENDED_PARAMETER param = { {0, 0}, {0} }; + param.Type.Type = MiMemExtendedParameterAddressRequirements; + param.Arg.Pointer = &reqs; + void* p = (*pVirtualAlloc2)(GetCurrentProcess(), addr, size, flags, PAGE_READWRITE, ¶m, 1); + if (p != NULL) return p; + _mi_warning_message("unable to allocate aligned OS memory (%zu bytes, error code: 0x%x, address: %p, alignment: %zu, flags: 0x%x)\n", size, GetLastError(), addr, try_alignment, flags); + // fall through on error + } + // last resort + return VirtualAlloc(addr, size, flags, PAGE_READWRITE); +} + +static void* win_virtual_alloc(void* addr, size_t size, size_t try_alignment, DWORD flags, bool large_only, bool allow_large, bool* is_large) { + mi_assert_internal(!(large_only && !allow_large)); + static _Atomic(size_t) large_page_try_ok; // = 0; + void* p = NULL; + // Try to allocate large OS pages (2MiB) if allowed or required. + if ((large_only || _mi_os_use_large_page(size, try_alignment)) + && allow_large && (flags&MEM_COMMIT)!=0 && (flags&MEM_RESERVE)!=0) { + size_t try_ok = mi_atomic_load_acquire(&large_page_try_ok); + if (!large_only && try_ok > 0) { + // if a large page allocation fails, it seems the calls to VirtualAlloc get very expensive. + // therefore, once a large page allocation failed, we don't try again for `large_page_try_ok` times. + mi_atomic_cas_strong_acq_rel(&large_page_try_ok, &try_ok, try_ok - 1); + } + else { + // large OS pages must always reserve and commit. + *is_large = true; + p = win_virtual_alloc_prim(addr, size, try_alignment, flags | MEM_LARGE_PAGES); + if (large_only) return p; + // fall back to non-large page allocation on error (`p == NULL`). + if (p == NULL) { + mi_atomic_store_release(&large_page_try_ok,10UL); // on error, don't try again for the next N allocations + } + } + } + // Fall back to regular page allocation + if (p == NULL) { + *is_large = ((flags&MEM_LARGE_PAGES) != 0); + p = win_virtual_alloc_prim(addr, size, try_alignment, flags); + } + //if (p == NULL) { _mi_warning_message("unable to allocate OS memory (%zu bytes, error code: 0x%x, address: %p, alignment: %zu, flags: 0x%x, large only: %d, allow large: %d)\n", size, GetLastError(), addr, try_alignment, flags, large_only, allow_large); } + return p; +} + +int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, void** addr) { + mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0); + mi_assert_internal(commit || !allow_large); + mi_assert_internal(try_alignment > 0); + int flags = MEM_RESERVE; + if (commit) { flags |= MEM_COMMIT; } + *addr = win_virtual_alloc(NULL, size, try_alignment, flags, false, allow_large, is_large); + return (*addr != NULL ? 0 : (int)GetLastError()); +} + + +//--------------------------------------------- +// Commit/Reset/Protect +//--------------------------------------------- +#ifdef _MSC_VER +#pragma warning(disable:6250) // suppress warning calling VirtualFree without MEM_RELEASE (for decommit) +#endif + +int _mi_prim_commit(void* addr, size_t size, bool commit) { + if (commit) { + void* p = VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE); + return (p == addr ? 0 : (int)GetLastError()); + } + else { + BOOL ok = VirtualFree(addr, size, MEM_DECOMMIT); + return (ok ? 0 : (int)GetLastError()); + } +} + +int _mi_prim_reset(void* addr, size_t size) { + void* p = VirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE); + mi_assert_internal(p == addr); + #if 1 + if (p == addr && addr != NULL) { + VirtualUnlock(addr,size); // VirtualUnlock after MEM_RESET removes the memory from the working set + } + #endif + return (p == addr ? 0 : (int)GetLastError()); +} + +int _mi_prim_protect(void* addr, size_t size, bool protect) { + DWORD oldprotect = 0; + BOOL ok = VirtualProtect(addr, size, protect ? PAGE_NOACCESS : PAGE_READWRITE, &oldprotect); + return (ok ? 0 : (int)GetLastError()); +} + + +//--------------------------------------------- +// Huge page allocation +//--------------------------------------------- + +static void* _mi_prim_alloc_huge_os_pagesx(void* hint_addr, size_t size, int numa_node) +{ + const DWORD flags = MEM_LARGE_PAGES | MEM_COMMIT | MEM_RESERVE; + + win_enable_large_os_pages(NULL); + + MI_MEM_EXTENDED_PARAMETER params[3] = { {{0,0},{0}},{{0,0},{0}},{{0,0},{0}} }; + // on modern Windows try use NtAllocateVirtualMemoryEx for 1GiB huge pages + static bool mi_huge_pages_available = true; + if (pNtAllocateVirtualMemoryEx != NULL && mi_huge_pages_available) { + params[0].Type.Type = MiMemExtendedParameterAttributeFlags; + params[0].Arg.ULong64 = MI_MEM_EXTENDED_PARAMETER_NONPAGED_HUGE; + ULONG param_count = 1; + if (numa_node >= 0) { + param_count++; + params[1].Type.Type = MiMemExtendedParameterNumaNode; + params[1].Arg.ULong = (unsigned)numa_node; + } + SIZE_T psize = size; + void* base = hint_addr; + NTSTATUS err = (*pNtAllocateVirtualMemoryEx)(GetCurrentProcess(), &base, &psize, flags, PAGE_READWRITE, params, param_count); + if (err == 0 && base != NULL) { + return base; + } + else { + // fall back to regular large pages + mi_huge_pages_available = false; // don't try further huge pages + _mi_warning_message("unable to allocate using huge (1GiB) pages, trying large (2MiB) pages instead (status 0x%lx)\n", err); + } + } + // on modern Windows try use VirtualAlloc2 for numa aware large OS page allocation + if (pVirtualAlloc2 != NULL && numa_node >= 0) { + params[0].Type.Type = MiMemExtendedParameterNumaNode; + params[0].Arg.ULong = (unsigned)numa_node; + return (*pVirtualAlloc2)(GetCurrentProcess(), hint_addr, size, flags, PAGE_READWRITE, params, 1); + } + + // otherwise use regular virtual alloc on older windows + return VirtualAlloc(hint_addr, size, flags, PAGE_READWRITE); +} + +int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, void** addr) { + *addr = _mi_prim_alloc_huge_os_pagesx(hint_addr,size,numa_node); + return (*addr != NULL ? 0 : (int)GetLastError()); +} + + +//--------------------------------------------- +// Numa nodes +//--------------------------------------------- + +size_t _mi_prim_numa_node(void) { + USHORT numa_node = 0; + if (pGetCurrentProcessorNumberEx != NULL && pGetNumaProcessorNodeEx != NULL) { + // Extended API is supported + MI_PROCESSOR_NUMBER pnum; + (*pGetCurrentProcessorNumberEx)(&pnum); + USHORT nnode = 0; + BOOL ok = (*pGetNumaProcessorNodeEx)(&pnum, &nnode); + if (ok) { numa_node = nnode; } + } + else if (pGetNumaProcessorNode != NULL) { + // Vista or earlier, use older API that is limited to 64 processors. Issue #277 + DWORD pnum = GetCurrentProcessorNumber(); + UCHAR nnode = 0; + BOOL ok = pGetNumaProcessorNode((UCHAR)pnum, &nnode); + if (ok) { numa_node = nnode; } + } + return numa_node; +} + +size_t _mi_prim_numa_node_count(void) { + ULONG numa_max = 0; + GetNumaHighestNodeNumber(&numa_max); + // find the highest node number that has actual processors assigned to it. Issue #282 + while(numa_max > 0) { + if (pGetNumaNodeProcessorMaskEx != NULL) { + // Extended API is supported + GROUP_AFFINITY affinity; + if ((*pGetNumaNodeProcessorMaskEx)((USHORT)numa_max, &affinity)) { + if (affinity.Mask != 0) break; // found the maximum non-empty node + } + } + else { + // Vista or earlier, use older API that is limited to 64 processors. + ULONGLONG mask; + if (GetNumaNodeProcessorMask((UCHAR)numa_max, &mask)) { + if (mask != 0) break; // found the maximum non-empty node + }; + } + // max node was invalid or had no processor assigned, try again + numa_max--; + } + return ((size_t)numa_max + 1); +} + + +//---------------------------------------------------------------- +// Clock +//---------------------------------------------------------------- + +static mi_msecs_t mi_to_msecs(LARGE_INTEGER t) { + static LARGE_INTEGER mfreq; // = 0 + if (mfreq.QuadPart == 0LL) { + LARGE_INTEGER f; + QueryPerformanceFrequency(&f); + mfreq.QuadPart = f.QuadPart/1000LL; + if (mfreq.QuadPart == 0) mfreq.QuadPart = 1; + } + return (mi_msecs_t)(t.QuadPart / mfreq.QuadPart); +} + +mi_msecs_t _mi_prim_clock_now(void) { + LARGE_INTEGER t; + QueryPerformanceCounter(&t); + return mi_to_msecs(t); +} + + +//---------------------------------------------------------------- +// Process Info +//---------------------------------------------------------------- + +#include +#include + +static mi_msecs_t filetime_msecs(const FILETIME* ftime) { + ULARGE_INTEGER i; + i.LowPart = ftime->dwLowDateTime; + i.HighPart = ftime->dwHighDateTime; + mi_msecs_t msecs = (i.QuadPart / 10000); // FILETIME is in 100 nano seconds + return msecs; +} + +typedef BOOL (WINAPI *PGetProcessMemoryInfo)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD); +static PGetProcessMemoryInfo pGetProcessMemoryInfo = NULL; + +void _mi_prim_process_info(mi_process_info_t* pinfo) +{ + FILETIME ct; + FILETIME ut; + FILETIME st; + FILETIME et; + GetProcessTimes(GetCurrentProcess(), &ct, &et, &st, &ut); + pinfo->utime = filetime_msecs(&ut); + pinfo->stime = filetime_msecs(&st); + + // load psapi on demand + if (pGetProcessMemoryInfo == NULL) { + HINSTANCE hDll = LoadLibrary(TEXT("psapi.dll")); + if (hDll != NULL) { + pGetProcessMemoryInfo = (PGetProcessMemoryInfo)(void (*)(void))GetProcAddress(hDll, "GetProcessMemoryInfo"); + } + } + + // get process info + PROCESS_MEMORY_COUNTERS info; + memset(&info, 0, sizeof(info)); + if (pGetProcessMemoryInfo != NULL) { + pGetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); + } + pinfo->current_rss = (size_t)info.WorkingSetSize; + pinfo->peak_rss = (size_t)info.PeakWorkingSetSize; + pinfo->current_commit = (size_t)info.PagefileUsage; + pinfo->peak_commit = (size_t)info.PeakPagefileUsage; + pinfo->page_faults = (size_t)info.PageFaultCount; +} + +//---------------------------------------------------------------- +// Output +//---------------------------------------------------------------- + +void _mi_prim_out_stderr( const char* msg ) +{ + // on windows with redirection, the C runtime cannot handle locale dependent output + // after the main thread closes so we use direct console output. + if (!_mi_preloading()) { + // _cputs(msg); // _cputs cannot be used at is aborts if it fails to lock the console + static HANDLE hcon = INVALID_HANDLE_VALUE; + static bool hconIsConsole; + if (hcon == INVALID_HANDLE_VALUE) { + CONSOLE_SCREEN_BUFFER_INFO sbi; + hcon = GetStdHandle(STD_ERROR_HANDLE); + hconIsConsole = ((hcon != INVALID_HANDLE_VALUE) && GetConsoleScreenBufferInfo(hcon, &sbi)); + } + const size_t len = _mi_strlen(msg); + if (len > 0 && len < UINT32_MAX) { + DWORD written = 0; + if (hconIsConsole) { + WriteConsoleA(hcon, msg, (DWORD)len, &written, NULL); + } + else if (hcon != INVALID_HANDLE_VALUE) { + // use direct write if stderr was redirected + WriteFile(hcon, msg, (DWORD)len, &written, NULL); + } + else { + // finally fall back to fputs after all + fputs(msg, stderr); + } + } + } +} + + +//---------------------------------------------------------------- +// Environment +//---------------------------------------------------------------- + +// On Windows use GetEnvironmentVariable instead of getenv to work +// reliably even when this is invoked before the C runtime is initialized. +// i.e. when `_mi_preloading() == true`. +// Note: on windows, environment names are not case sensitive. +bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { + result[0] = 0; + size_t len = GetEnvironmentVariableA(name, result, (DWORD)result_size); + return (len > 0 && len < result_size); +} + + + +//---------------------------------------------------------------- +// Random +//---------------------------------------------------------------- + +#if defined(MI_USE_RTLGENRANDOM) // || defined(__cplusplus) +// We prefer to use BCryptGenRandom instead of (the unofficial) RtlGenRandom but when using +// dynamic overriding, we observed it can raise an exception when compiled with C++, and +// sometimes deadlocks when also running under the VS debugger. +// In contrast, issue #623 implies that on Windows Server 2019 we need to use BCryptGenRandom. +// To be continued.. +#pragma comment (lib,"advapi32.lib") +#define RtlGenRandom SystemFunction036 +mi_decl_externc BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength); + +bool _mi_prim_random_buf(void* buf, size_t buf_len) { + return (RtlGenRandom(buf, (ULONG)buf_len) != 0); +} + +#else + +#ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG +#define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002 +#endif + +typedef LONG (NTAPI *PBCryptGenRandom)(HANDLE, PUCHAR, ULONG, ULONG); +static PBCryptGenRandom pBCryptGenRandom = NULL; + +bool _mi_prim_random_buf(void* buf, size_t buf_len) { + if (pBCryptGenRandom == NULL) { + HINSTANCE hDll = LoadLibrary(TEXT("bcrypt.dll")); + if (hDll != NULL) { + pBCryptGenRandom = (PBCryptGenRandom)(void (*)(void))GetProcAddress(hDll, "BCryptGenRandom"); + } + if (pBCryptGenRandom == NULL) return false; + } + return (pBCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)buf_len, BCRYPT_USE_SYSTEM_PREFERRED_RNG) >= 0); +} + +#endif // MI_USE_RTLGENRANDOM + +//---------------------------------------------------------------- +// Thread init/done +//---------------------------------------------------------------- + +#if !defined(MI_SHARED_LIB) + +// use thread local storage keys to detect thread ending +#include +#if (_WIN32_WINNT < 0x600) // before Windows Vista +WINBASEAPI DWORD WINAPI FlsAlloc( _In_opt_ PFLS_CALLBACK_FUNCTION lpCallback ); +WINBASEAPI PVOID WINAPI FlsGetValue( _In_ DWORD dwFlsIndex ); +WINBASEAPI BOOL WINAPI FlsSetValue( _In_ DWORD dwFlsIndex, _In_opt_ PVOID lpFlsData ); +WINBASEAPI BOOL WINAPI FlsFree(_In_ DWORD dwFlsIndex); +#endif + +static DWORD mi_fls_key = (DWORD)(-1); + +static void NTAPI mi_fls_done(PVOID value) { + mi_heap_t* heap = (mi_heap_t*)value; + if (heap != NULL) { + _mi_thread_done(heap); + FlsSetValue(mi_fls_key, NULL); // prevent recursion as _mi_thread_done may set it back to the main heap, issue #672 + } +} + +void _mi_prim_thread_init_auto_done(void) { + mi_fls_key = FlsAlloc(&mi_fls_done); +} + +void _mi_prim_thread_done_auto_done(void) { + // call thread-done on all threads (except the main thread) to prevent + // dangling callback pointer if statically linked with a DLL; Issue #208 + FlsFree(mi_fls_key); +} + +void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { + mi_assert_internal(mi_fls_key != (DWORD)(-1)); + FlsSetValue(mi_fls_key, heap); +} + +#else + +// Dll; nothing to do as in that case thread_done is handled through the DLL_THREAD_DETACH event. + +void _mi_prim_thread_init_auto_done(void) { +} + +void _mi_prim_thread_done_auto_done(void) { +} + +void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { + MI_UNUSED(heap); +} + +#endif diff --git a/Source/mimalloc/src/prim/windows/readme.md b/Source/mimalloc/src/prim/windows/readme.md new file mode 100644 index 000000000..217c3d174 --- /dev/null +++ b/Source/mimalloc/src/prim/windows/readme.md @@ -0,0 +1,17 @@ +## Primitives: + +- `prim.c` contains Windows primitives for OS allocation. + +## Event Tracing for Windows (ETW) + +- `etw.h` is generated from `etw.man` which contains the manifest for mimalloc events. + (100 is an allocation, 101 is for a free) + +- `etw-mimalloc.wprp` is a profile for the Windows Performance Recorder (WPR). + In an admin prompt, you can use: + ``` + > wpr -start src\prim\windows\etw-mimalloc.wprp -filemode + > + > wpr -stop test.etl + ``` + and then open `test.etl` in the Windows Performance Analyzer (WPA). \ No newline at end of file diff --git a/Source/mimalloc/src/random.c b/Source/mimalloc/src/random.c index 255bede4d..4fc8b2f8f 100644 --- a/Source/mimalloc/src/random.c +++ b/Source/mimalloc/src/random.c @@ -5,9 +5,9 @@ terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ #include "mimalloc.h" -#include "mimalloc-internal.h" - -#include // memset +#include "mimalloc/internal.h" +#include "mimalloc/prim.h" // _mi_prim_random_buf +#include // memset /* ---------------------------------------------------------------------------- We use our own PRNG to keep predictable performance of random number generation @@ -154,118 +154,13 @@ uintptr_t _mi_random_next(mi_random_ctx_t* ctx) { /* ---------------------------------------------------------------------------- -To initialize a fresh random context we rely on the OS: -- Windows : BCryptGenRandom (or RtlGenRandom) -- osX,bsd,wasi: arc4random_buf -- Linux : getrandom,/dev/urandom +To initialize a fresh random context. If we cannot get good randomness, we fall back to weak randomness based on a timer and ASLR. -----------------------------------------------------------------------------*/ -#if defined(_WIN32) - -#if !defined(MI_USE_RTLGENRANDOM) -// We prefer BCryptGenRandom over RtlGenRandom -#pragma comment (lib,"bcrypt.lib") -#include -static bool os_random_buf(void* buf, size_t buf_len) { - return (BCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)buf_len, BCRYPT_USE_SYSTEM_PREFERRED_RNG) >= 0); -} -#else -// Use (unofficial) RtlGenRandom -#pragma comment (lib,"advapi32.lib") -#define RtlGenRandom SystemFunction036 -#ifdef __cplusplus -extern "C" { -#endif -BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength); -#ifdef __cplusplus -} -#endif -static bool os_random_buf(void* buf, size_t buf_len) { - return (RtlGenRandom(buf, (ULONG)buf_len) != 0); -} -#endif - -#elif defined(ANDROID) || defined(XP_DARWIN) || defined(__APPLE__) || defined(__DragonFly__) || \ - defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ - defined(__sun) || defined(__wasi__) -#include -static bool os_random_buf(void* buf, size_t buf_len) { - arc4random_buf(buf, buf_len); - return true; -} -#elif defined(__linux__) -#include -#include -#include -#include -#include -#include -static bool os_random_buf(void* buf, size_t buf_len) { - // Modern Linux provides `getrandom` but different distributions either use `sys/random.h` or `linux/random.h` - // and for the latter the actual `getrandom` call is not always defined. - // (see ) - // We therefore use a syscall directly and fall back dynamically to /dev/urandom when needed. -#ifdef SYS_getrandom - #ifndef GRND_NONBLOCK - #define GRND_NONBLOCK (1) - #endif - static _Atomic(uintptr_t) no_getrandom; // = 0 - if (mi_atomic_load_acquire(&no_getrandom)==0) { - ssize_t ret = syscall(SYS_getrandom, buf, buf_len, GRND_NONBLOCK); - if (ret >= 0) return (buf_len == (size_t)ret); - if (ret != ENOSYS) return false; - mi_atomic_store_release(&no_getrandom, 1UL); // don't call again, and fall back to /dev/urandom - } -#endif - int flags = O_RDONLY; - #if defined(O_CLOEXEC) - flags |= O_CLOEXEC; - #endif - int fd = open("/dev/urandom", flags, 0); - if (fd < 0) return false; - size_t count = 0; - while(count < buf_len) { - ssize_t ret = read(fd, (char*)buf + count, buf_len - count); - if (ret<=0) { - if (errno!=EAGAIN && errno!=EINTR) break; - } - else { - count += ret; - } - } - close(fd); - return (count==buf_len); -} -#else -static bool os_random_buf(void* buf, size_t buf_len) { - return false; -} -#endif - -#if defined(_WIN32) -#include -#elif defined(__APPLE__) -#include -#else -#include -#endif - -uintptr_t _os_random_weak(uintptr_t extra_seed) { - uintptr_t x = (uintptr_t)&_os_random_weak ^ extra_seed; // ASLR makes the address random - - #if defined(_WIN32) - LARGE_INTEGER pcount; - QueryPerformanceCounter(&pcount); - x ^= (uintptr_t)(pcount.QuadPart); - #elif defined(__APPLE__) - x ^= (uintptr_t)mach_absolute_time(); - #else - struct timespec time; - clock_gettime(CLOCK_MONOTONIC, &time); - x ^= (uintptr_t)time.tv_sec; - x ^= (uintptr_t)time.tv_nsec; - #endif +uintptr_t _mi_os_random_weak(uintptr_t extra_seed) { + uintptr_t x = (uintptr_t)&_mi_os_random_weak ^ extra_seed; // ASLR makes the address random + x ^= _mi_prim_clock_now(); // and do a few randomization steps uintptr_t max = ((x ^ (x >> 17)) & 0x0F) + 1; for (uintptr_t i = 0; i < max; i++) { @@ -275,21 +170,41 @@ uintptr_t _os_random_weak(uintptr_t extra_seed) { return x; } -void _mi_random_init(mi_random_ctx_t* ctx) { +static void mi_random_init_ex(mi_random_ctx_t* ctx, bool use_weak) { uint8_t key[32]; - if (!os_random_buf(key, sizeof(key))) { + if (use_weak || !_mi_prim_random_buf(key, sizeof(key))) { // if we fail to get random data from the OS, we fall back to a // weak random source based on the current time - _mi_warning_message("unable to use secure randomness\n"); - uintptr_t x = _os_random_weak(0); + #if !defined(__wasi__) + if (!use_weak) { _mi_warning_message("unable to use secure randomness\n"); } + #endif + uintptr_t x = _mi_os_random_weak(0); for (size_t i = 0; i < 8; i++) { // key is eight 32-bit words. x = _mi_random_shuffle(x); ((uint32_t*)key)[i] = (uint32_t)x; } + ctx->weak = true; + } + else { + ctx->weak = false; } chacha_init(ctx, key, (uintptr_t)ctx /*nonce*/ ); } +void _mi_random_init(mi_random_ctx_t* ctx) { + mi_random_init_ex(ctx, false); +} + +void _mi_random_init_weak(mi_random_ctx_t * ctx) { + mi_random_init_ex(ctx, true); +} + +void _mi_random_reinit_if_weak(mi_random_ctx_t * ctx) { + if (ctx->weak) { + _mi_random_init(ctx); + } +} + /* -------------------------------------------------------- test vectors from ----------------------------------------------------------- */ diff --git a/Source/mimalloc/src/region.c b/Source/mimalloc/src/region.c index 795407309..6c8ffb79c 100644 --- a/Source/mimalloc/src/region.c +++ b/Source/mimalloc/src/region.c @@ -16,8 +16,8 @@ We need this memory layer between the raw OS calls because of: 1. on `sbrk` like systems (like WebAssembly) we need our own memory maps in order to reuse memory effectively. 2. It turns out that for large objects, between 1MiB and 32MiB (?), the cost of - an OS allocation/free is still (much) too expensive relative to the accesses - in that object :-( (`malloc-large` tests this). This means we need a cheaper + an OS allocation/free is still (much) too expensive relative to the accesses + in that object :-( (`malloc-large` tests this). This means we need a cheaper way to reuse memory. 3. This layer allows for NUMA aware allocation. @@ -32,49 +32,34 @@ Possible issues: do this better without adding too much complexity? -----------------------------------------------------------------------------*/ #include "mimalloc.h" -#include "mimalloc-internal.h" -#include "mimalloc-atomic.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" #include // memset #include "bitmap.h" -// Internal raw OS interface -size_t _mi_os_large_page_size(); -bool _mi_os_protect(void* addr, size_t size); -bool _mi_os_unprotect(void* addr, size_t size); -bool _mi_os_commit(void* p, size_t size, bool* is_zero, mi_stats_t* stats); -bool _mi_os_decommit(void* p, size_t size, mi_stats_t* stats); -bool _mi_os_reset(void* p, size_t size, mi_stats_t* stats); -bool _mi_os_unreset(void* p, size_t size, bool* is_zero, mi_stats_t* stats); - -// arena.c -void _mi_arena_free(void* p, size_t size, size_t memid, bool all_committed, mi_stats_t* stats); -void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld); -void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld); - - +// os.c +bool _mi_os_unreset(void* addr, size_t size, bool* is_zero, mi_stats_t* tld_stats); // Constants #if (MI_INTPTR_SIZE==8) -#define MI_HEAP_REGION_MAX_SIZE (256 * GiB) // 64KiB for the region map +#define MI_HEAP_REGION_MAX_SIZE (256 * MI_GiB) // 64KiB for the region map #elif (MI_INTPTR_SIZE==4) -#define MI_HEAP_REGION_MAX_SIZE (3 * GiB) // ~ KiB for the region map +#define MI_HEAP_REGION_MAX_SIZE (3 * MI_GiB) // ~ KiB for the region map #else #error "define the maximum heap space allowed for regions on this platform" #endif -#define MI_SEGMENT_ALIGN MI_SEGMENT_SIZE - #define MI_REGION_MAX_BLOCKS MI_BITMAP_FIELD_BITS #define MI_REGION_SIZE (MI_SEGMENT_SIZE * MI_BITMAP_FIELD_BITS) // 256MiB (64MiB on 32 bits) #define MI_REGION_MAX (MI_HEAP_REGION_MAX_SIZE / MI_REGION_SIZE) // 1024 (48 on 32 bits) #define MI_REGION_MAX_OBJ_BLOCKS (MI_REGION_MAX_BLOCKS/4) // 64MiB -#define MI_REGION_MAX_OBJ_SIZE (MI_REGION_MAX_OBJ_BLOCKS*MI_SEGMENT_SIZE) +#define MI_REGION_MAX_OBJ_SIZE (MI_REGION_MAX_OBJ_BLOCKS*MI_SEGMENT_SIZE) -// Region info +// Region info typedef union mi_region_info_u { - uintptr_t value; + size_t value; struct { bool valid; // initialized? bool is_large:1; // allocated in fixed large/huge OS pages @@ -87,21 +72,21 @@ typedef union mi_region_info_u { // A region owns a chunk of REGION_SIZE (256MiB) (virtual) memory with // a bit map with one bit per MI_SEGMENT_SIZE (4MiB) block. typedef struct mem_region_s { - _Atomic(uintptr_t) info; // mi_region_info_t.value - _Atomic(void*) start; // start of the memory area + _Atomic(size_t) info; // mi_region_info_t.value + _Atomic(void*) start; // start of the memory area mi_bitmap_field_t in_use; // bit per in-use block mi_bitmap_field_t dirty; // track if non-zero per block mi_bitmap_field_t commit; // track if committed per block mi_bitmap_field_t reset; // track if reset per block - _Atomic(uintptr_t) arena_memid; // if allocated from a (huge page) arena - uintptr_t padding; // round to 8 fields + _Atomic(size_t) arena_memid; // if allocated from a (huge page) arena + _Atomic(size_t) padding; // round to 8 fields (needs to be atomic for msvc, see issue #508) } mem_region_t; // The region map static mem_region_t regions[MI_REGION_MAX]; // Allocated regions -static _Atomic(uintptr_t) regions_count; // = 0; +static _Atomic(size_t) regions_count; // = 0; /* ---------------------------------------------------------------------------- @@ -122,7 +107,7 @@ static size_t mi_good_commit_size(size_t size) { */ // Return if a pointer points into a region reserved by us. -bool mi_is_in_heap_region(const void* p) mi_attr_noexcept { +mi_decl_nodiscard bool mi_is_in_heap_region(const void* p) mi_attr_noexcept { if (p==NULL) return false; size_t count = mi_atomic_load_relaxed(®ions_count); for (size_t i = 0; i < count; i++) { @@ -136,7 +121,7 @@ bool mi_is_in_heap_region(const void* p) mi_attr_noexcept { static void* mi_region_blocks_start(const mem_region_t* region, mi_bitmap_index_t bit_idx) { uint8_t* start = (uint8_t*)mi_atomic_load_ptr_acquire(uint8_t, &((mem_region_t*)region)->start); mi_assert_internal(start != NULL); - return (start + (bit_idx * MI_SEGMENT_SIZE)); + return (start + (bit_idx * MI_SEGMENT_SIZE)); } static size_t mi_memid_create(mem_region_t* region, mi_bitmap_index_t bit_idx) { @@ -180,32 +165,32 @@ static bool mi_region_try_alloc_os(size_t blocks, bool commit, bool allow_large, bool is_zero = false; bool is_pinned = false; size_t arena_memid = 0; - void* const start = _mi_arena_alloc_aligned(MI_REGION_SIZE, MI_SEGMENT_ALIGN, ®ion_commit, ®ion_large, &is_pinned, &is_zero, &arena_memid, tld); + void* const start = _mi_arena_alloc_aligned(MI_REGION_SIZE, MI_SEGMENT_ALIGN, 0, ®ion_commit, ®ion_large, &is_pinned, &is_zero, _mi_arena_id_none(), & arena_memid, tld); if (start == NULL) return false; mi_assert_internal(!(region_large && !allow_large)); mi_assert_internal(!region_large || region_commit); // claim a fresh slot - const uintptr_t idx = mi_atomic_increment_acq_rel(®ions_count); + const size_t idx = mi_atomic_increment_acq_rel(®ions_count); if (idx >= MI_REGION_MAX) { mi_atomic_decrement_acq_rel(®ions_count); - _mi_arena_free(start, MI_REGION_SIZE, arena_memid, region_commit, tld->stats); - _mi_warning_message("maximum regions used: %zu GiB (perhaps recompile with a larger setting for MI_HEAP_REGION_MAX_SIZE)", _mi_divide_up(MI_HEAP_REGION_MAX_SIZE, GiB)); + _mi_arena_free(start, MI_REGION_SIZE, MI_SEGMENT_ALIGN, 0, arena_memid, region_commit, tld->stats); + _mi_warning_message("maximum regions used: %zu GiB (perhaps recompile with a larger setting for MI_HEAP_REGION_MAX_SIZE)", _mi_divide_up(MI_HEAP_REGION_MAX_SIZE, MI_GiB)); return false; } // allocated, initialize and claim the initial blocks mem_region_t* r = ®ions[idx]; r->arena_memid = arena_memid; - mi_atomic_store_release(&r->in_use, (uintptr_t)0); + mi_atomic_store_release(&r->in_use, (size_t)0); mi_atomic_store_release(&r->dirty, (is_zero ? 0 : MI_BITMAP_FIELD_FULL)); mi_atomic_store_release(&r->commit, (region_commit ? MI_BITMAP_FIELD_FULL : 0)); - mi_atomic_store_release(&r->reset, (uintptr_t)0); + mi_atomic_store_release(&r->reset, (size_t)0); *bit_idx = 0; _mi_bitmap_claim(&r->in_use, 1, blocks, *bit_idx, NULL); mi_atomic_store_ptr_release(void,&r->start, start); - // and share it + // and share it mi_region_info_t info; info.value = 0; // initialize the full union to zero info.x.valid = true; @@ -242,7 +227,7 @@ static bool mi_region_is_suitable(const mem_region_t* region, int numa_node, boo static bool mi_region_try_claim(int numa_node, size_t blocks, bool allow_large, mem_region_t** region, mi_bitmap_index_t* bit_idx, mi_os_tld_t* tld) { - // try all regions for a free slot + // try all regions for a free slot const size_t count = mi_atomic_load_relaxed(®ions_count); // monotonic, so ok to be relaxed size_t idx = tld->region_idx; // Or start at 0 to reuse low addresses? Starting at 0 seems to increase latency though for (size_t visited = 0; visited < count; visited++, idx++) { @@ -276,7 +261,7 @@ static void* mi_region_try_alloc(size_t blocks, bool* commit, bool* large, bool* return NULL; } } - + // ------------------------------------------------ // found a region and claimed `blocks` at `bit_idx`, initialize them now mi_assert_internal(region != NULL); @@ -288,7 +273,7 @@ static void* mi_region_try_alloc(size_t blocks, bool* commit, bool* large, bool* mi_assert_internal(!(info.x.is_large && !*large)); mi_assert_internal(start != NULL); - *is_zero = _mi_bitmap_claim(®ion->dirty, 1, blocks, bit_idx, NULL); + *is_zero = _mi_bitmap_claim(®ion->dirty, 1, blocks, bit_idx, NULL); *large = info.x.is_large; *is_pinned = info.x.is_pinned; *memid = mi_memid_create(region, bit_idx); @@ -307,20 +292,20 @@ static void* mi_region_try_alloc(size_t blocks, bool* commit, bool* large, bool* mi_bitmap_unclaim(®ion->in_use, 1, blocks, bit_idx); return NULL; } - if (commit_zero) *is_zero = true; + if (commit_zero) *is_zero = true; } } else { // no need to commit, but check if already fully committed *commit = _mi_bitmap_is_claimed(®ion->commit, 1, blocks, bit_idx); - } + } mi_assert_internal(!*commit || _mi_bitmap_is_claimed(®ion->commit, 1, blocks, bit_idx)); // unreset reset blocks if (_mi_bitmap_is_any_claimed(®ion->reset, 1, blocks, bit_idx)) { // some blocks are still reset mi_assert_internal(!info.x.is_large && !info.x.is_pinned); - mi_assert_internal(!mi_option_is_enabled(mi_option_eager_commit) || *commit || mi_option_get(mi_option_eager_commit_delay) > 0); + mi_assert_internal(!mi_option_is_enabled(mi_option_eager_commit) || *commit || mi_option_get(mi_option_eager_commit_delay) > 0); mi_bitmap_unclaim(®ion->reset, 1, blocks, bit_idx); if (*commit || !mi_option_is_enabled(mi_option_reset_decommits)) { // only if needed bool reset_zero = false; @@ -329,13 +314,13 @@ static void* mi_region_try_alloc(size_t blocks, bool* commit, bool* large, bool* } } mi_assert_internal(!_mi_bitmap_is_any_claimed(®ion->reset, 1, blocks, bit_idx)); - - #if (MI_DEBUG>=2) + + #if (MI_DEBUG>=2) && !MI_TRACK_ENABLED // && !MI_TSAN if (*commit) { ((uint8_t*)p)[0] = 0; } #endif - - // and return the allocation - mi_assert_internal(p != NULL); + + // and return the allocation + mi_assert_internal(p != NULL); return p; } @@ -346,7 +331,7 @@ static void* mi_region_try_alloc(size_t blocks, bool* commit, bool* large, bool* // Allocate `size` memory aligned at `alignment`. Return non NULL on success, with a given memory `id`. // (`id` is abstract, but `id = idx*MI_REGION_MAP_BITS + bitidx`) -void* _mi_mem_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld) +void* _mi_mem_alloc_aligned(size_t size, size_t alignment, size_t align_offset, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld) { mi_assert_internal(memid != NULL && tld != NULL); mi_assert_internal(size > 0); @@ -354,7 +339,7 @@ void* _mi_mem_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* l *is_zero = false; *is_pinned = false; bool default_large = false; - if (large==NULL) large = &default_large; // ensure `large != NULL` + if (large==NULL) large = &default_large; // ensure `large != NULL` if (size == 0) return NULL; size = _mi_align_up(size, _mi_os_page_size()); @@ -362,23 +347,23 @@ void* _mi_mem_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* l void* p = NULL; size_t arena_memid; const size_t blocks = mi_region_block_count(size); - if (blocks <= MI_REGION_MAX_OBJ_BLOCKS && alignment <= MI_SEGMENT_ALIGN) { - p = mi_region_try_alloc(blocks, commit, large, is_pinned, is_zero, memid, tld); + if (blocks <= MI_REGION_MAX_OBJ_BLOCKS && alignment <= MI_SEGMENT_ALIGN && align_offset == 0) { + p = mi_region_try_alloc(blocks, commit, large, is_pinned, is_zero, memid, tld); if (p == NULL) { _mi_warning_message("unable to allocate from region: size %zu\n", size); } } if (p == NULL) { // and otherwise fall back to the OS - p = _mi_arena_alloc_aligned(size, alignment, commit, large, is_pinned, is_zero, &arena_memid, tld); + p = _mi_arena_alloc_aligned(size, alignment, align_offset, commit, large, is_pinned, is_zero, _mi_arena_id_none(), & arena_memid, tld); *memid = mi_memid_create_from_arena(arena_memid); } if (p != NULL) { - mi_assert_internal((uintptr_t)p % alignment == 0); -#if (MI_DEBUG>=2) + mi_assert_internal(((uintptr_t)p + align_offset) % alignment == 0); + #if (MI_DEBUG>=2) && !MI_TRACK_ENABLED // && !MI_TSAN if (*commit) { ((uint8_t*)p)[0] = 0; } // ensure the memory is committed -#endif + #endif } return p; } @@ -390,21 +375,22 @@ Free -----------------------------------------------------------------------------*/ // Free previously allocated memory with a given id. -void _mi_mem_free(void* p, size_t size, size_t id, bool full_commit, bool any_reset, mi_os_tld_t* tld) { +void _mi_mem_free(void* p, size_t size, size_t alignment, size_t align_offset, size_t id, bool full_commit, bool any_reset, mi_os_tld_t* tld) { mi_assert_internal(size > 0 && tld != NULL); if (p==NULL) return; if (size==0) return; size = _mi_align_up(size, _mi_os_page_size()); - + size_t arena_memid = 0; mi_bitmap_index_t bit_idx; mem_region_t* region; if (mi_memid_is_arena(id,®ion,&bit_idx,&arena_memid)) { // was a direct arena allocation, pass through - _mi_arena_free(p, size, arena_memid, full_commit, tld->stats); + _mi_arena_free(p, size, alignment, align_offset, arena_memid, full_commit, tld->stats); } else { // allocated in a region + mi_assert_internal(align_offset == 0); mi_assert_internal(size <= MI_REGION_MAX_OBJ_SIZE); if (size > MI_REGION_MAX_OBJ_SIZE) return; const size_t blocks = mi_region_block_count(size); mi_assert_internal(blocks + bit_idx <= MI_BITMAP_FIELD_BITS); @@ -427,9 +413,9 @@ void _mi_mem_free(void* p, size_t size, size_t id, bool full_commit, bool any_re } // reset the blocks to reduce the working set. - if (!info.x.is_large && !info.x.is_pinned && mi_option_is_enabled(mi_option_segment_reset) + if (!info.x.is_large && !info.x.is_pinned && mi_option_is_enabled(mi_option_segment_reset) && (mi_option_is_enabled(mi_option_eager_commit) || - mi_option_is_enabled(mi_option_reset_decommits))) // cannot reset halfway committed segments, use only `option_page_reset` instead + mi_option_is_enabled(mi_option_reset_decommits))) // cannot reset halfway committed segments, use only `option_page_reset` instead { bool any_unreset; _mi_bitmap_claim(®ion->reset, 1, blocks, bit_idx, &any_unreset); @@ -437,11 +423,11 @@ void _mi_mem_free(void* p, size_t size, size_t id, bool full_commit, bool any_re _mi_abandoned_await_readers(); // ensure no more pending write (in case reset = decommit) _mi_mem_reset(p, blocks * MI_SEGMENT_SIZE, tld); } - } + } // and unclaim bool all_unclaimed = mi_bitmap_unclaim(®ion->in_use, 1, blocks, bit_idx); - mi_assert_internal(all_unclaimed); UNUSED(all_unclaimed); + mi_assert_internal(all_unclaimed); MI_UNUSED(all_unclaimed); } } @@ -451,24 +437,24 @@ void _mi_mem_free(void* p, size_t size, size_t id, bool full_commit, bool any_re -----------------------------------------------------------------------------*/ void _mi_mem_collect(mi_os_tld_t* tld) { // free every region that has no segments in use. - uintptr_t rcount = mi_atomic_load_relaxed(®ions_count); + size_t rcount = mi_atomic_load_relaxed(®ions_count); for (size_t i = 0; i < rcount; i++) { mem_region_t* region = ®ions[i]; if (mi_atomic_load_relaxed(®ion->info) != 0) { // if no segments used, try to claim the whole region - uintptr_t m = mi_atomic_load_relaxed(®ion->in_use); + size_t m = mi_atomic_load_relaxed(®ion->in_use); while (m == 0 && !mi_atomic_cas_weak_release(®ion->in_use, &m, MI_BITMAP_FIELD_FULL)) { /* nothing */ }; if (m == 0) { // on success, free the whole region uint8_t* start = (uint8_t*)mi_atomic_load_ptr_acquire(uint8_t,®ions[i].start); size_t arena_memid = mi_atomic_load_relaxed(®ions[i].arena_memid); - uintptr_t commit = mi_atomic_load_relaxed(®ions[i].commit); - memset(®ions[i], 0, sizeof(mem_region_t)); + size_t commit = mi_atomic_load_relaxed(®ions[i].commit); + memset((void*)®ions[i], 0, sizeof(mem_region_t)); // cast to void* to avoid atomic warning // and release the whole region - mi_atomic_store_release(®ion->info, (uintptr_t)0); - if (start != NULL) { // && !_mi_os_is_huge_reserved(start)) { + mi_atomic_store_release(®ion->info, (size_t)0); + if (start != NULL) { // && !_mi_os_is_huge_reserved(start)) { _mi_abandoned_await_readers(); // ensure no pending reads - _mi_arena_free(start, MI_REGION_SIZE, arena_memid, (~commit == 0), tld->stats); + _mi_arena_free(start, MI_REGION_SIZE, MI_SEGMENT_ALIGN, 0, arena_memid, (~commit == 0), tld->stats); } } } @@ -481,11 +467,21 @@ void _mi_mem_collect(mi_os_tld_t* tld) { -----------------------------------------------------------------------------*/ bool _mi_mem_reset(void* p, size_t size, mi_os_tld_t* tld) { - return _mi_os_reset(p, size, tld->stats); + if (mi_option_is_enabled(mi_option_reset_decommits)) { + return _mi_os_decommit(p, size, tld->stats); + } + else { + return _mi_os_reset(p, size, tld->stats); + } } bool _mi_mem_unreset(void* p, size_t size, bool* is_zero, mi_os_tld_t* tld) { - return _mi_os_unreset(p, size, is_zero, tld->stats); + if (mi_option_is_enabled(mi_option_reset_decommits)) { + return _mi_os_commit(p, size, is_zero, tld->stats); + } + else { + return _mi_os_unreset(p, size, is_zero, tld->stats); + } } bool _mi_mem_commit(void* p, size_t size, bool* is_zero, mi_os_tld_t* tld) { diff --git a/Source/mimalloc/src/segment-cache.c b/Source/mimalloc/src/segment-cache.c new file mode 100644 index 000000000..eeae1b508 --- /dev/null +++ b/Source/mimalloc/src/segment-cache.c @@ -0,0 +1,423 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2020, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +/* ---------------------------------------------------------------------------- + Implements a cache of segments to avoid expensive OS calls and to reuse + the commit_mask to optimize the commit/decommit calls. + The full memory map of all segments is also implemented here. +-----------------------------------------------------------------------------*/ +#include "mimalloc.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" + +#include "./bitmap.h" // atomic bitmap + +//#define MI_CACHE_DISABLE 1 // define to completely disable the segment cache + +#define MI_CACHE_FIELDS (16) +#define MI_CACHE_MAX (MI_BITMAP_FIELD_BITS*MI_CACHE_FIELDS) // 1024 on 64-bit + +#define BITS_SET() MI_ATOMIC_VAR_INIT(UINTPTR_MAX) +#define MI_CACHE_BITS_SET MI_INIT16(BITS_SET) // note: update if MI_CACHE_FIELDS changes + +typedef struct mi_cache_slot_s { + void* p; + size_t memid; + bool is_pinned; + mi_commit_mask_t commit_mask; + mi_commit_mask_t decommit_mask; + _Atomic(mi_msecs_t) expire; +} mi_cache_slot_t; + +static mi_decl_cache_align mi_cache_slot_t cache[MI_CACHE_MAX]; // = 0 + +static mi_decl_cache_align mi_bitmap_field_t cache_unavailable[MI_CACHE_FIELDS] = { MI_CACHE_BITS_SET }; // zero bit = available! +static mi_decl_cache_align mi_bitmap_field_t cache_unavailable_large[MI_CACHE_FIELDS] = { MI_CACHE_BITS_SET }; +static mi_decl_cache_align mi_bitmap_field_t cache_inuse[MI_CACHE_FIELDS]; // zero bit = free + +static bool mi_cdecl mi_segment_cache_is_suitable(mi_bitmap_index_t bitidx, void* arg) { + mi_arena_id_t req_arena_id = *((mi_arena_id_t*)arg); + mi_cache_slot_t* slot = &cache[mi_bitmap_index_bit(bitidx)]; + return _mi_arena_memid_is_suitable(slot->memid, req_arena_id); +} + +mi_decl_noinline static void* mi_segment_cache_pop_ex( + bool all_suitable, + size_t size, mi_commit_mask_t* commit_mask, + mi_commit_mask_t* decommit_mask, bool large_allowed, + bool* large, bool* is_pinned, bool* is_zero, + mi_arena_id_t _req_arena_id, size_t* memid, mi_os_tld_t* tld) +{ +#ifdef MI_CACHE_DISABLE + return NULL; +#else + + // only segment blocks + if (size != MI_SEGMENT_SIZE) return NULL; + + // numa node determines start field + const int numa_node = _mi_os_numa_node(tld); + size_t start_field = 0; + if (numa_node > 0) { + start_field = (MI_CACHE_FIELDS / _mi_os_numa_node_count())*numa_node; + if (start_field >= MI_CACHE_FIELDS) start_field = 0; + } + + // find an available slot and make it unavailable + mi_bitmap_index_t bitidx = 0; + bool claimed = false; + mi_arena_id_t req_arena_id = _req_arena_id; + mi_bitmap_pred_fun_t pred_fun = (all_suitable ? NULL : &mi_segment_cache_is_suitable); // cannot pass NULL as the arena may be exclusive itself; todo: do not put exclusive arenas in the cache? + + if (large_allowed) { // large allowed? + claimed = _mi_bitmap_try_find_from_claim_pred(cache_unavailable_large, MI_CACHE_FIELDS, start_field, 1, pred_fun, &req_arena_id, &bitidx); + if (claimed) *large = true; + } + if (!claimed) { + claimed = _mi_bitmap_try_find_from_claim_pred (cache_unavailable, MI_CACHE_FIELDS, start_field, 1, pred_fun, &req_arena_id, &bitidx); + if (claimed) *large = false; + } + + if (!claimed) return NULL; + + // no longer available but still in-use + mi_assert_internal(_mi_bitmap_is_claimed(cache_unavailable, MI_CACHE_FIELDS, 1, bitidx)); + mi_assert_internal(_mi_bitmap_is_claimed(cache_unavailable_large, MI_CACHE_FIELDS, 1, bitidx)); + mi_assert_internal(_mi_bitmap_is_claimed(cache_inuse, MI_CACHE_FIELDS, 1, bitidx)); + + // found a slot + mi_cache_slot_t* slot = &cache[mi_bitmap_index_bit(bitidx)]; + void* p = slot->p; + *memid = slot->memid; + *is_pinned = slot->is_pinned; + *is_zero = false; + *commit_mask = slot->commit_mask; + *decommit_mask = slot->decommit_mask; + slot->p = NULL; + mi_atomic_storei64_release(&slot->expire,(mi_msecs_t)0); + + // mark the slot as free again + _mi_bitmap_unclaim(cache_inuse, MI_CACHE_FIELDS, 1, bitidx); + return p; +#endif +} + + +mi_decl_noinline void* _mi_segment_cache_pop(size_t size, mi_commit_mask_t* commit_mask, mi_commit_mask_t* decommit_mask, bool large_allowed, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t _req_arena_id, size_t* memid, mi_os_tld_t* tld) +{ + return mi_segment_cache_pop_ex(false, size, commit_mask, decommit_mask, large_allowed, large, is_pinned, is_zero, _req_arena_id, memid, tld); +} + +static mi_decl_noinline void mi_commit_mask_decommit(mi_commit_mask_t* cmask, void* p, size_t total, mi_stats_t* stats) +{ + if (mi_commit_mask_is_empty(cmask)) { + // nothing + } + else if (mi_commit_mask_is_full(cmask)) { + // decommit the whole in one call + _mi_os_decommit(p, total, stats); + } + else { + // decommit parts + mi_assert_internal((total%MI_COMMIT_MASK_BITS)==0); + size_t part = total/MI_COMMIT_MASK_BITS; + size_t idx; + size_t count; + mi_commit_mask_foreach(cmask, idx, count) { + void* start = (uint8_t*)p + (idx*part); + size_t size = count*part; + _mi_os_decommit(start, size, stats); + } + mi_commit_mask_foreach_end() + } + mi_commit_mask_create_empty(cmask); +} + +#define MI_MAX_PURGE_PER_PUSH (4) + +static mi_decl_noinline void mi_segment_cache_purge(bool visit_all, bool force, mi_os_tld_t* tld) +{ + MI_UNUSED(tld); + if (!mi_option_is_enabled(mi_option_allow_decommit)) return; + mi_msecs_t now = _mi_clock_now(); + size_t purged = 0; + const size_t max_visits = (visit_all ? MI_CACHE_MAX /* visit all */ : MI_CACHE_FIELDS /* probe at most N (=16) slots */); + size_t idx = (visit_all ? 0 : _mi_random_shuffle((uintptr_t)now) % MI_CACHE_MAX /* random start */ ); + for (size_t visited = 0; visited < max_visits; visited++,idx++) { // visit N slots + if (idx >= MI_CACHE_MAX) idx = 0; // wrap + mi_cache_slot_t* slot = &cache[idx]; + mi_msecs_t expire = mi_atomic_loadi64_relaxed(&slot->expire); + if (expire != 0 && (force || now >= expire)) { // racy read + // seems expired, first claim it from available + purged++; + mi_bitmap_index_t bitidx = mi_bitmap_index_create_from_bit(idx); + if (_mi_bitmap_claim(cache_unavailable, MI_CACHE_FIELDS, 1, bitidx, NULL)) { // no need to check large as those cannot be decommitted anyways + // it was available, we claimed it (and made it unavailable) + mi_assert_internal(_mi_bitmap_is_claimed(cache_unavailable, MI_CACHE_FIELDS, 1, bitidx)); + mi_assert_internal(_mi_bitmap_is_claimed(cache_unavailable_large, MI_CACHE_FIELDS, 1, bitidx)); + // we can now access it safely + expire = mi_atomic_loadi64_acquire(&slot->expire); + if (expire != 0 && (force || now >= expire)) { // safe read + mi_assert_internal(_mi_bitmap_is_claimed(cache_inuse, MI_CACHE_FIELDS, 1, bitidx)); + // still expired, decommit it + mi_atomic_storei64_relaxed(&slot->expire,(mi_msecs_t)0); + mi_assert_internal(!mi_commit_mask_is_empty(&slot->commit_mask)); + _mi_abandoned_await_readers(); // wait until safe to decommit + // decommit committed parts + // TODO: instead of decommit, we could also free to the OS? + mi_commit_mask_decommit(&slot->commit_mask, slot->p, MI_SEGMENT_SIZE, tld->stats); + mi_commit_mask_create_empty(&slot->decommit_mask); + } + _mi_bitmap_unclaim(cache_unavailable, MI_CACHE_FIELDS, 1, bitidx); // make it available again for a pop + } + if (!visit_all && purged > MI_MAX_PURGE_PER_PUSH) break; // bound to no more than N purge tries per push + } + } +} + +void _mi_segment_cache_collect(bool force, mi_os_tld_t* tld) { + if (force) { + // called on `mi_collect(true)` but not on thread termination + _mi_segment_cache_free_all(tld); + } + else { + mi_segment_cache_purge(true /* visit all */, false /* don't force unexpired */, tld); + } +} + +void _mi_segment_cache_free_all(mi_os_tld_t* tld) { + mi_commit_mask_t commit_mask; + mi_commit_mask_t decommit_mask; + bool is_pinned; + bool is_zero; + bool is_large; + size_t memid; + const size_t size = MI_SEGMENT_SIZE; + void* p; + do { + // keep popping and freeing the memory + p = mi_segment_cache_pop_ex(true /* all */, size, &commit_mask, &decommit_mask, + true /* allow large */, &is_large, &is_pinned, &is_zero, _mi_arena_id_none(), &memid, tld); + if (p != NULL) { + size_t csize = _mi_commit_mask_committed_size(&commit_mask, size); + if (csize > 0 && !is_pinned) { _mi_stat_decrease(&_mi_stats_main.committed, csize); } + _mi_arena_free(p, size, MI_SEGMENT_ALIGN, 0, memid, is_pinned /* pretend not committed to not double count decommits */, tld->stats); + } + } while (p != NULL); +} + +mi_decl_noinline bool _mi_segment_cache_push(void* start, size_t size, size_t memid, const mi_commit_mask_t* commit_mask, const mi_commit_mask_t* decommit_mask, bool is_large, bool is_pinned, mi_os_tld_t* tld) +{ +#ifdef MI_CACHE_DISABLE + return false; +#else + + // purge expired entries + mi_segment_cache_purge(false /* limit purges to a constant N */, false /* don't force unexpired */, tld); + + // only cache normal segment blocks + if (size != MI_SEGMENT_SIZE || ((uintptr_t)start % MI_SEGMENT_ALIGN) != 0) return false; + + // Also do not cache arena allocated segments that cannot be decommitted. (as arena allocation is fast) + // This is a common case with reserved huge OS pages. + // + // (note: we could also allow segments that are already fully decommitted but that never happens + // as the first slice is always committed (for the segment metadata)) + if (!_mi_arena_is_os_allocated(memid) && is_pinned) return false; + + // numa node determines start field + int numa_node = _mi_os_numa_node(NULL); + size_t start_field = 0; + if (numa_node > 0) { + start_field = (MI_CACHE_FIELDS / _mi_os_numa_node_count()) * numa_node; + if (start_field >= MI_CACHE_FIELDS) start_field = 0; + } + + // find an available slot + mi_bitmap_index_t bitidx; + bool claimed = _mi_bitmap_try_find_from_claim(cache_inuse, MI_CACHE_FIELDS, start_field, 1, &bitidx); + if (!claimed) return false; + + mi_assert_internal(_mi_bitmap_is_claimed(cache_unavailable, MI_CACHE_FIELDS, 1, bitidx)); + mi_assert_internal(_mi_bitmap_is_claimed(cache_unavailable_large, MI_CACHE_FIELDS, 1, bitidx)); +#if MI_DEBUG>1 + if (is_pinned || is_large) { + mi_assert_internal(mi_commit_mask_is_full(commit_mask)); + } +#endif + + // set the slot + mi_cache_slot_t* slot = &cache[mi_bitmap_index_bit(bitidx)]; + slot->p = start; + slot->memid = memid; + slot->is_pinned = is_pinned; + mi_atomic_storei64_relaxed(&slot->expire,(mi_msecs_t)0); + slot->commit_mask = *commit_mask; + slot->decommit_mask = *decommit_mask; + if (!mi_commit_mask_is_empty(commit_mask) && !is_large && !is_pinned && mi_option_is_enabled(mi_option_allow_decommit)) { + long delay = mi_option_get(mi_option_segment_decommit_delay); + if (delay == 0) { + _mi_abandoned_await_readers(); // wait until safe to decommit + mi_commit_mask_decommit(&slot->commit_mask, start, MI_SEGMENT_SIZE, tld->stats); + mi_commit_mask_create_empty(&slot->decommit_mask); + } + else { + mi_atomic_storei64_release(&slot->expire, _mi_clock_now() + delay); + } + } + + // make it available + _mi_bitmap_unclaim((is_large ? cache_unavailable_large : cache_unavailable), MI_CACHE_FIELDS, 1, bitidx); + return true; +#endif +} + + +/* ----------------------------------------------------------- + The following functions are to reliably find the segment or + block that encompasses any pointer p (or NULL if it is not + in any of our segments). + We maintain a bitmap of all memory with 1 bit per MI_SEGMENT_SIZE (64MiB) + set to 1 if it contains the segment meta data. +----------------------------------------------------------- */ + + +#if (MI_INTPTR_SIZE==8) +#define MI_MAX_ADDRESS ((size_t)40 << 40) // 20TB +#else +#define MI_MAX_ADDRESS ((size_t)2 << 30) // 2Gb +#endif + +#define MI_SEGMENT_MAP_BITS (MI_MAX_ADDRESS / MI_SEGMENT_SIZE) +#define MI_SEGMENT_MAP_SIZE (MI_SEGMENT_MAP_BITS / 8) +#define MI_SEGMENT_MAP_WSIZE (MI_SEGMENT_MAP_SIZE / MI_INTPTR_SIZE) + +static _Atomic(uintptr_t) mi_segment_map[MI_SEGMENT_MAP_WSIZE + 1]; // 2KiB per TB with 64MiB segments + +static size_t mi_segment_map_index_of(const mi_segment_t* segment, size_t* bitidx) { + mi_assert_internal(_mi_ptr_segment(segment + 1) == segment); // is it aligned on MI_SEGMENT_SIZE? + if ((uintptr_t)segment >= MI_MAX_ADDRESS) { + *bitidx = 0; + return MI_SEGMENT_MAP_WSIZE; + } + else { + const uintptr_t segindex = ((uintptr_t)segment) / MI_SEGMENT_SIZE; + *bitidx = segindex % MI_INTPTR_BITS; + const size_t mapindex = segindex / MI_INTPTR_BITS; + mi_assert_internal(mapindex < MI_SEGMENT_MAP_WSIZE); + return mapindex; + } +} + +void _mi_segment_map_allocated_at(const mi_segment_t* segment) { + size_t bitidx; + size_t index = mi_segment_map_index_of(segment, &bitidx); + mi_assert_internal(index <= MI_SEGMENT_MAP_WSIZE); + if (index==MI_SEGMENT_MAP_WSIZE) return; + uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]); + uintptr_t newmask; + do { + newmask = (mask | ((uintptr_t)1 << bitidx)); + } while (!mi_atomic_cas_weak_release(&mi_segment_map[index], &mask, newmask)); +} + +void _mi_segment_map_freed_at(const mi_segment_t* segment) { + size_t bitidx; + size_t index = mi_segment_map_index_of(segment, &bitidx); + mi_assert_internal(index <= MI_SEGMENT_MAP_WSIZE); + if (index == MI_SEGMENT_MAP_WSIZE) return; + uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]); + uintptr_t newmask; + do { + newmask = (mask & ~((uintptr_t)1 << bitidx)); + } while (!mi_atomic_cas_weak_release(&mi_segment_map[index], &mask, newmask)); +} + +// Determine the segment belonging to a pointer or NULL if it is not in a valid segment. +static mi_segment_t* _mi_segment_of(const void* p) { + if (p == NULL) return NULL; + mi_segment_t* segment = _mi_ptr_segment(p); + mi_assert_internal(segment != NULL); + size_t bitidx; + size_t index = mi_segment_map_index_of(segment, &bitidx); + // fast path: for any pointer to valid small/medium/large object or first MI_SEGMENT_SIZE in huge + const uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]); + if mi_likely((mask & ((uintptr_t)1 << bitidx)) != 0) { + return segment; // yes, allocated by us + } + if (index==MI_SEGMENT_MAP_WSIZE) return NULL; + + // TODO: maintain max/min allocated range for efficiency for more efficient rejection of invalid pointers? + + // search downwards for the first segment in case it is an interior pointer + // could be slow but searches in MI_INTPTR_SIZE * MI_SEGMENT_SIZE (512MiB) steps trough + // valid huge objects + // note: we could maintain a lowest index to speed up the path for invalid pointers? + size_t lobitidx; + size_t loindex; + uintptr_t lobits = mask & (((uintptr_t)1 << bitidx) - 1); + if (lobits != 0) { + loindex = index; + lobitidx = mi_bsr(lobits); // lobits != 0 + } + else if (index == 0) { + return NULL; + } + else { + mi_assert_internal(index > 0); + uintptr_t lomask = mask; + loindex = index; + do { + loindex--; + lomask = mi_atomic_load_relaxed(&mi_segment_map[loindex]); + } while (lomask != 0 && loindex > 0); + if (lomask == 0) return NULL; + lobitidx = mi_bsr(lomask); // lomask != 0 + } + mi_assert_internal(loindex < MI_SEGMENT_MAP_WSIZE); + // take difference as the addresses could be larger than the MAX_ADDRESS space. + size_t diff = (((index - loindex) * (8*MI_INTPTR_SIZE)) + bitidx - lobitidx) * MI_SEGMENT_SIZE; + segment = (mi_segment_t*)((uint8_t*)segment - diff); + + if (segment == NULL) return NULL; + mi_assert_internal((void*)segment < p); + bool cookie_ok = (_mi_ptr_cookie(segment) == segment->cookie); + mi_assert_internal(cookie_ok); + if mi_unlikely(!cookie_ok) return NULL; + if (((uint8_t*)segment + mi_segment_size(segment)) <= (uint8_t*)p) return NULL; // outside the range + mi_assert_internal(p >= (void*)segment && (uint8_t*)p < (uint8_t*)segment + mi_segment_size(segment)); + return segment; +} + +// Is this a valid pointer in our heap? +static bool mi_is_valid_pointer(const void* p) { + return (_mi_segment_of(p) != NULL); +} + +mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept { + return mi_is_valid_pointer(p); +} + +/* +// Return the full segment range belonging to a pointer +static void* mi_segment_range_of(const void* p, size_t* size) { + mi_segment_t* segment = _mi_segment_of(p); + if (segment == NULL) { + if (size != NULL) *size = 0; + return NULL; + } + else { + if (size != NULL) *size = segment->segment_size; + return segment; + } + mi_assert_expensive(page == NULL || mi_segment_is_valid(_mi_page_segment(page),tld)); + mi_assert_internal(page == NULL || (mi_segment_page_size(_mi_page_segment(page)) - (MI_SECURE == 0 ? 0 : _mi_os_page_size())) >= block_size); + mi_reset_delayed(tld); + mi_assert_internal(page == NULL || mi_page_not_in_queue(page, tld)); + return page; +} +*/ diff --git a/Source/mimalloc/src/segment.c b/Source/mimalloc/src/segment.c index 1d59be9d0..3e56d50f5 100644 --- a/Source/mimalloc/src/segment.c +++ b/Source/mimalloc/src/segment.c @@ -5,445 +5,359 @@ terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ #include "mimalloc.h" -#include "mimalloc-internal.h" -#include "mimalloc-atomic.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" #include // memset #include #define MI_PAGE_HUGE_ALIGN (256*1024) -static uint8_t* mi_segment_raw_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size); +static void mi_segment_delayed_decommit(mi_segment_t* segment, bool force, mi_stats_t* stats); -/* -------------------------------------------------------------------------------- - Segment allocation - We allocate pages inside bigger "segments" (4mb on 64-bit). This is to avoid - splitting VMA's on Linux and reduce fragmentation on other OS's. - Each thread owns its own segments. - - Currently we have: - - small pages (64kb), 64 in one segment - - medium pages (512kb), 8 in one segment - - large pages (4mb), 1 in one segment - - huge blocks > MI_LARGE_OBJ_SIZE_MAX become large segment with 1 page - - In any case the memory for a segment is virtual and usually committed on demand. - (i.e. we are careful to not touch the memory until we actually allocate a block there) - - If a thread ends, it "abandons" pages with used blocks - and there is an abandoned segment list whose segments can - be reclaimed by still running threads, much like work-stealing. --------------------------------------------------------------------------------- */ +// ------------------------------------------------------------------- +// commit mask +// ------------------------------------------------------------------- -/* ----------------------------------------------------------- - Queue of segments containing free pages ------------------------------------------------------------ */ - -#if (MI_DEBUG>=3) -static bool mi_segment_queue_contains(const mi_segment_queue_t* queue, const mi_segment_t* segment) { - mi_assert_internal(segment != NULL); - mi_segment_t* list = queue->first; - while (list != NULL) { - if (list == segment) break; - mi_assert_internal(list->next==NULL || list->next->prev == list); - mi_assert_internal(list->prev==NULL || list->prev->next == list); - list = list->next; +static bool mi_commit_mask_all_set(const mi_commit_mask_t* commit, const mi_commit_mask_t* cm) { + for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { + if ((commit->mask[i] & cm->mask[i]) != cm->mask[i]) return false; } - return (list == segment); -} -#endif - -static bool mi_segment_queue_is_empty(const mi_segment_queue_t* queue) { - return (queue->first == NULL); -} - -static void mi_segment_queue_remove(mi_segment_queue_t* queue, mi_segment_t* segment) { - mi_assert_expensive(mi_segment_queue_contains(queue, segment)); - if (segment->prev != NULL) segment->prev->next = segment->next; - if (segment->next != NULL) segment->next->prev = segment->prev; - if (segment == queue->first) queue->first = segment->next; - if (segment == queue->last) queue->last = segment->prev; - segment->next = NULL; - segment->prev = NULL; + return true; } -static void mi_segment_enqueue(mi_segment_queue_t* queue, mi_segment_t* segment) { - mi_assert_expensive(!mi_segment_queue_contains(queue, segment)); - segment->next = NULL; - segment->prev = queue->last; - if (queue->last != NULL) { - mi_assert_internal(queue->last->next == NULL); - queue->last->next = segment; - queue->last = segment; +static bool mi_commit_mask_any_set(const mi_commit_mask_t* commit, const mi_commit_mask_t* cm) { + for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { + if ((commit->mask[i] & cm->mask[i]) != 0) return true; } - else { - queue->last = queue->first = segment; - } -} - -static mi_segment_queue_t* mi_segment_free_queue_of_kind(mi_page_kind_t kind, mi_segments_tld_t* tld) { - if (kind == MI_PAGE_SMALL) return &tld->small_free; - else if (kind == MI_PAGE_MEDIUM) return &tld->medium_free; - else return NULL; -} - -static mi_segment_queue_t* mi_segment_free_queue(const mi_segment_t* segment, mi_segments_tld_t* tld) { - return mi_segment_free_queue_of_kind(segment->page_kind, tld); + return false; } -// remove from free queue if it is in one -static void mi_segment_remove_from_free_queue(mi_segment_t* segment, mi_segments_tld_t* tld) { - mi_segment_queue_t* queue = mi_segment_free_queue(segment, tld); // may be NULL - bool in_queue = (queue!=NULL && (segment->next != NULL || segment->prev != NULL || queue->first == segment)); - if (in_queue) { - mi_segment_queue_remove(queue, segment); +static void mi_commit_mask_create_intersect(const mi_commit_mask_t* commit, const mi_commit_mask_t* cm, mi_commit_mask_t* res) { + for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { + res->mask[i] = (commit->mask[i] & cm->mask[i]); } } -static void mi_segment_insert_in_free_queue(mi_segment_t* segment, mi_segments_tld_t* tld) { - mi_segment_enqueue(mi_segment_free_queue(segment, tld), segment); +static void mi_commit_mask_clear(mi_commit_mask_t* res, const mi_commit_mask_t* cm) { + for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { + res->mask[i] &= ~(cm->mask[i]); + } } - -/* ----------------------------------------------------------- - Invariant checking ------------------------------------------------------------ */ - -#if (MI_DEBUG>=2) -static bool mi_segment_is_in_free_queue(const mi_segment_t* segment, mi_segments_tld_t* tld) { - mi_segment_queue_t* queue = mi_segment_free_queue(segment, tld); - bool in_queue = (queue!=NULL && (segment->next != NULL || segment->prev != NULL || queue->first == segment)); - if (in_queue) { - mi_assert_expensive(mi_segment_queue_contains(queue, segment)); +static void mi_commit_mask_set(mi_commit_mask_t* res, const mi_commit_mask_t* cm) { + for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { + res->mask[i] |= cm->mask[i]; } - return in_queue; } -#endif -static size_t mi_segment_page_size(const mi_segment_t* segment) { - if (segment->capacity > 1) { - mi_assert_internal(segment->page_kind <= MI_PAGE_MEDIUM); - return ((size_t)1 << segment->page_shift); +static void mi_commit_mask_create(size_t bitidx, size_t bitcount, mi_commit_mask_t* cm) { + mi_assert_internal(bitidx < MI_COMMIT_MASK_BITS); + mi_assert_internal((bitidx + bitcount) <= MI_COMMIT_MASK_BITS); + if (bitcount == MI_COMMIT_MASK_BITS) { + mi_assert_internal(bitidx==0); + mi_commit_mask_create_full(cm); + } + else if (bitcount == 0) { + mi_commit_mask_create_empty(cm); } else { - mi_assert_internal(segment->page_kind >= MI_PAGE_LARGE); - return segment->segment_size; + mi_commit_mask_create_empty(cm); + size_t i = bitidx / MI_COMMIT_MASK_FIELD_BITS; + size_t ofs = bitidx % MI_COMMIT_MASK_FIELD_BITS; + while (bitcount > 0) { + mi_assert_internal(i < MI_COMMIT_MASK_FIELD_COUNT); + size_t avail = MI_COMMIT_MASK_FIELD_BITS - ofs; + size_t count = (bitcount > avail ? avail : bitcount); + size_t mask = (count >= MI_COMMIT_MASK_FIELD_BITS ? ~((size_t)0) : (((size_t)1 << count) - 1) << ofs); + cm->mask[i] = mask; + bitcount -= count; + ofs = 0; + i++; + } } } - -#if (MI_DEBUG>=2) -static bool mi_pages_reset_contains(const mi_page_t* page, mi_segments_tld_t* tld) { - mi_page_t* p = tld->pages_reset.first; - while (p != NULL) { - if (p == page) return true; - p = p->next; - } - return false; -} -#endif - -#if (MI_DEBUG>=3) -static bool mi_segment_is_valid(const mi_segment_t* segment, mi_segments_tld_t* tld) { - mi_assert_internal(segment != NULL); - mi_assert_internal(_mi_ptr_cookie(segment) == segment->cookie); - mi_assert_internal(segment->used <= segment->capacity); - mi_assert_internal(segment->abandoned <= segment->used); - size_t nfree = 0; - for (size_t i = 0; i < segment->capacity; i++) { - const mi_page_t* const page = &segment->pages[i]; - if (!page->segment_in_use) { - nfree++; +size_t _mi_commit_mask_committed_size(const mi_commit_mask_t* cm, size_t total) { + mi_assert_internal((total%MI_COMMIT_MASK_BITS)==0); + size_t count = 0; + for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) { + size_t mask = cm->mask[i]; + if (~mask == 0) { + count += MI_COMMIT_MASK_FIELD_BITS; } - if (page->segment_in_use || page->is_reset) { - mi_assert_expensive(!mi_pages_reset_contains(page, tld)); + else { + for (; mask != 0; mask >>= 1) { // todo: use popcount + if ((mask&1)!=0) count++; + } } } - mi_assert_internal(nfree + segment->used == segment->capacity); - // mi_assert_internal(segment->thread_id == _mi_thread_id() || (segment->thread_id==0)); // or 0 - mi_assert_internal(segment->page_kind == MI_PAGE_HUGE || - (mi_segment_page_size(segment) * segment->capacity == segment->segment_size)); - return true; + // we use total since for huge segments each commit bit may represent a larger size + return ((total / MI_COMMIT_MASK_BITS) * count); } -#endif -static bool mi_page_not_in_queue(const mi_page_t* page, mi_segments_tld_t* tld) { - mi_assert_internal(page != NULL); - if (page->next != NULL || page->prev != NULL) { - mi_assert_internal(mi_pages_reset_contains(page, tld)); - return false; + +size_t _mi_commit_mask_next_run(const mi_commit_mask_t* cm, size_t* idx) { + size_t i = (*idx) / MI_COMMIT_MASK_FIELD_BITS; + size_t ofs = (*idx) % MI_COMMIT_MASK_FIELD_BITS; + size_t mask = 0; + // find first ones + while (i < MI_COMMIT_MASK_FIELD_COUNT) { + mask = cm->mask[i]; + mask >>= ofs; + if (mask != 0) { + while ((mask&1) == 0) { + mask >>= 1; + ofs++; + } + break; + } + i++; + ofs = 0; + } + if (i >= MI_COMMIT_MASK_FIELD_COUNT) { + // not found + *idx = MI_COMMIT_MASK_BITS; + return 0; } else { - // both next and prev are NULL, check for singleton list - return (tld->pages_reset.first != page && tld->pages_reset.last != page); + // found, count ones + size_t count = 0; + *idx = (i*MI_COMMIT_MASK_FIELD_BITS) + ofs; + do { + mi_assert_internal(ofs < MI_COMMIT_MASK_FIELD_BITS && (mask&1) == 1); + do { + count++; + mask >>= 1; + } while ((mask&1) == 1); + if ((((*idx + count) % MI_COMMIT_MASK_FIELD_BITS) == 0)) { + i++; + if (i >= MI_COMMIT_MASK_FIELD_COUNT) break; + mask = cm->mask[i]; + ofs = 0; + } + } while ((mask&1) == 1); + mi_assert_internal(count > 0); + return count; } } +/* -------------------------------------------------------------------------------- + Segment allocation + + If a thread ends, it "abandons" pages with used blocks + and there is an abandoned segment list whose segments can + be reclaimed by still running threads, much like work-stealing. +-------------------------------------------------------------------------------- */ + + /* ----------------------------------------------------------- - Guard pages + Slices ----------------------------------------------------------- */ -static void mi_segment_protect_range(void* p, size_t size, bool protect) { - if (protect) { - _mi_mem_protect(p, size); - } - else { - _mi_mem_unprotect(p, size); - } -} - -static void mi_segment_protect(mi_segment_t* segment, bool protect, mi_os_tld_t* tld) { - // add/remove guard pages - if (MI_SECURE != 0) { - // in secure mode, we set up a protected page in between the segment info and the page data - const size_t os_psize = _mi_os_page_size(); - mi_assert_internal((segment->segment_info_size - os_psize) >= (sizeof(mi_segment_t) + ((segment->capacity - 1) * sizeof(mi_page_t)))); - mi_assert_internal(((uintptr_t)segment + segment->segment_info_size) % os_psize == 0); - mi_segment_protect_range((uint8_t*)segment + segment->segment_info_size - os_psize, os_psize, protect); - if (MI_SECURE <= 1 || segment->capacity == 1) { - // and protect the last (or only) page too - mi_assert_internal(MI_SECURE <= 1 || segment->page_kind >= MI_PAGE_LARGE); - uint8_t* start = (uint8_t*)segment + segment->segment_size - os_psize; - if (protect && !segment->mem_is_committed) { - if (protect) { - // ensure secure page is committed - if (_mi_mem_commit(start, os_psize, NULL, tld)) { // if this fails that is ok (as it is an unaccessible page) - mi_segment_protect_range(start, os_psize, protect); - } - } - } - else { - mi_segment_protect_range(start, os_psize, protect); - } - } - else { - // or protect every page - const size_t page_size = mi_segment_page_size(segment); - for (size_t i = 0; i < segment->capacity; i++) { - if (segment->pages[i].is_committed) { - mi_segment_protect_range((uint8_t*)segment + (i+1)*page_size - os_psize, os_psize, protect); - } - } - } - } + +static const mi_slice_t* mi_segment_slices_end(const mi_segment_t* segment) { + return &segment->slices[segment->slice_entries]; +} + +static uint8_t* mi_slice_start(const mi_slice_t* slice) { + mi_segment_t* segment = _mi_ptr_segment(slice); + mi_assert_internal(slice >= segment->slices && slice < mi_segment_slices_end(segment)); + return ((uint8_t*)segment + ((slice - segment->slices)*MI_SEGMENT_SLICE_SIZE)); } + /* ----------------------------------------------------------- - Page reset + Bins ----------------------------------------------------------- */ +// Use bit scan forward to quickly find the first zero bit if it is available + +static inline size_t mi_slice_bin8(size_t slice_count) { + if (slice_count<=1) return slice_count; + mi_assert_internal(slice_count <= MI_SLICES_PER_SEGMENT); + slice_count--; + size_t s = mi_bsr(slice_count); // slice_count > 1 + if (s <= 2) return slice_count + 1; + size_t bin = ((s << 2) | ((slice_count >> (s - 2))&0x03)) - 4; + return bin; +} -static void mi_page_reset(mi_segment_t* segment, mi_page_t* page, size_t size, mi_segments_tld_t* tld) { - mi_assert_internal(page->is_committed); - if (!mi_option_is_enabled(mi_option_page_reset)) return; - if (segment->mem_is_pinned || page->segment_in_use || !page->is_committed || page->is_reset) return; - size_t psize; - void* start = mi_segment_raw_page_start(segment, page, &psize); - page->is_reset = true; - mi_assert_internal(size <= psize); - size_t reset_size = ((size == 0 || size > psize) ? psize : size); - if (reset_size > 0) _mi_mem_reset(start, reset_size, tld->os); +static inline size_t mi_slice_bin(size_t slice_count) { + mi_assert_internal(slice_count*MI_SEGMENT_SLICE_SIZE <= MI_SEGMENT_SIZE); + mi_assert_internal(mi_slice_bin8(MI_SLICES_PER_SEGMENT) <= MI_SEGMENT_BIN_MAX); + size_t bin = mi_slice_bin8(slice_count); + mi_assert_internal(bin <= MI_SEGMENT_BIN_MAX); + return bin; } -static bool mi_page_unreset(mi_segment_t* segment, mi_page_t* page, size_t size, mi_segments_tld_t* tld) -{ - mi_assert_internal(page->is_reset); - mi_assert_internal(page->is_committed); - mi_assert_internal(!segment->mem_is_pinned); - if (segment->mem_is_pinned || !page->is_committed || !page->is_reset) return true; - page->is_reset = false; - size_t psize; - uint8_t* start = mi_segment_raw_page_start(segment, page, &psize); - size_t unreset_size = (size == 0 || size > psize ? psize : size); - bool is_zero = false; - bool ok = true; - if (unreset_size > 0) { - ok = _mi_mem_unreset(start, unreset_size, &is_zero, tld->os); - } - if (is_zero) page->is_zero_init = true; - return ok; +static inline size_t mi_slice_index(const mi_slice_t* slice) { + mi_segment_t* segment = _mi_ptr_segment(slice); + ptrdiff_t index = slice - segment->slices; + mi_assert_internal(index >= 0 && index < (ptrdiff_t)segment->slice_entries); + return index; } /* ----------------------------------------------------------- - The free page queue + Slice span queues ----------------------------------------------------------- */ -// we re-use the `used` field for the expiration counter. Since this is a -// a 32-bit field while the clock is always 64-bit we need to guard -// against overflow, we use substraction to check for expiry which work -// as long as the reset delay is under (2^30 - 1) milliseconds (~12 days) -static void mi_page_reset_set_expire(mi_page_t* page) { - uint32_t expire = (uint32_t)_mi_clock_now() + mi_option_get(mi_option_reset_delay); - page->used = expire; +static void mi_span_queue_push(mi_span_queue_t* sq, mi_slice_t* slice) { + // todo: or push to the end? + mi_assert_internal(slice->prev == NULL && slice->next==NULL); + slice->prev = NULL; // paranoia + slice->next = sq->first; + sq->first = slice; + if (slice->next != NULL) slice->next->prev = slice; + else sq->last = slice; + slice->xblock_size = 0; // free } -static bool mi_page_reset_is_expired(mi_page_t* page, mi_msecs_t now) { - int32_t expire = (int32_t)(page->used); - return (((int32_t)now - expire) >= 0); +static mi_span_queue_t* mi_span_queue_for(size_t slice_count, mi_segments_tld_t* tld) { + size_t bin = mi_slice_bin(slice_count); + mi_span_queue_t* sq = &tld->spans[bin]; + mi_assert_internal(sq->slice_count >= slice_count); + return sq; } -static void mi_pages_reset_add(mi_segment_t* segment, mi_page_t* page, mi_segments_tld_t* tld) { - mi_assert_internal(!page->segment_in_use || !page->is_committed); - mi_assert_internal(mi_page_not_in_queue(page,tld)); - mi_assert_expensive(!mi_pages_reset_contains(page, tld)); - mi_assert_internal(_mi_page_segment(page)==segment); - if (!mi_option_is_enabled(mi_option_page_reset)) return; - if (segment->mem_is_pinned || page->segment_in_use || !page->is_committed || page->is_reset) return; +static void mi_span_queue_delete(mi_span_queue_t* sq, mi_slice_t* slice) { + mi_assert_internal(slice->xblock_size==0 && slice->slice_count>0 && slice->slice_offset==0); + // should work too if the queue does not contain slice (which can happen during reclaim) + if (slice->prev != NULL) slice->prev->next = slice->next; + if (slice == sq->first) sq->first = slice->next; + if (slice->next != NULL) slice->next->prev = slice->prev; + if (slice == sq->last) sq->last = slice->prev; + slice->prev = NULL; + slice->next = NULL; + slice->xblock_size = 1; // no more free +} - if (mi_option_get(mi_option_reset_delay) == 0) { - // reset immediately? - mi_page_reset(segment, page, 0, tld); - } - else { - // otherwise push on the delayed page reset queue - mi_page_queue_t* pq = &tld->pages_reset; - // push on top - mi_page_reset_set_expire(page); - page->next = pq->first; - page->prev = NULL; - if (pq->first == NULL) { - mi_assert_internal(pq->last == NULL); - pq->first = pq->last = page; - } - else { - pq->first->prev = page; - pq->first = page; - } - } + +/* ----------------------------------------------------------- + Invariant checking +----------------------------------------------------------- */ + +static bool mi_slice_is_used(const mi_slice_t* slice) { + return (slice->xblock_size > 0); } -static void mi_pages_reset_remove(mi_page_t* page, mi_segments_tld_t* tld) { - if (mi_page_not_in_queue(page,tld)) return; - mi_page_queue_t* pq = &tld->pages_reset; - mi_assert_internal(pq!=NULL); - mi_assert_internal(!page->segment_in_use); - mi_assert_internal(mi_pages_reset_contains(page, tld)); - if (page->prev != NULL) page->prev->next = page->next; - if (page->next != NULL) page->next->prev = page->prev; - if (page == pq->last) pq->last = page->prev; - if (page == pq->first) pq->first = page->next; - page->next = page->prev = NULL; - page->used = 0; +#if (MI_DEBUG>=3) +static bool mi_span_queue_contains(mi_span_queue_t* sq, mi_slice_t* slice) { + for (mi_slice_t* s = sq->first; s != NULL; s = s->next) { + if (s==slice) return true; + } + return false; } -static void mi_pages_reset_remove_all_in_segment(mi_segment_t* segment, bool force_reset, mi_segments_tld_t* tld) { - if (segment->mem_is_pinned) return; // never reset in huge OS pages - for (size_t i = 0; i < segment->capacity; i++) { - mi_page_t* page = &segment->pages[i]; - if (!page->segment_in_use && page->is_committed && !page->is_reset) { - mi_pages_reset_remove(page, tld); - if (force_reset) { - mi_page_reset(segment, page, 0, tld); +static bool mi_segment_is_valid(mi_segment_t* segment, mi_segments_tld_t* tld) { + mi_assert_internal(segment != NULL); + mi_assert_internal(_mi_ptr_cookie(segment) == segment->cookie); + mi_assert_internal(segment->abandoned <= segment->used); + mi_assert_internal(segment->thread_id == 0 || segment->thread_id == _mi_thread_id()); + mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->decommit_mask)); // can only decommit committed blocks + //mi_assert_internal(segment->segment_info_size % MI_SEGMENT_SLICE_SIZE == 0); + mi_slice_t* slice = &segment->slices[0]; + const mi_slice_t* end = mi_segment_slices_end(segment); + size_t used_count = 0; + mi_span_queue_t* sq; + while(slice < end) { + mi_assert_internal(slice->slice_count > 0); + mi_assert_internal(slice->slice_offset == 0); + size_t index = mi_slice_index(slice); + size_t maxindex = (index + slice->slice_count >= segment->slice_entries ? segment->slice_entries : index + slice->slice_count) - 1; + if (mi_slice_is_used(slice)) { // a page in use, we need at least MAX_SLICE_OFFSET valid back offsets + used_count++; + for (size_t i = 0; i <= MI_MAX_SLICE_OFFSET && index + i <= maxindex; i++) { + mi_assert_internal(segment->slices[index + i].slice_offset == i*sizeof(mi_slice_t)); + mi_assert_internal(i==0 || segment->slices[index + i].slice_count == 0); + mi_assert_internal(i==0 || segment->slices[index + i].xblock_size == 1); + } + // and the last entry as well (for coalescing) + const mi_slice_t* last = slice + slice->slice_count - 1; + if (last > slice && last < mi_segment_slices_end(segment)) { + mi_assert_internal(last->slice_offset == (slice->slice_count-1)*sizeof(mi_slice_t)); + mi_assert_internal(last->slice_count == 0); + mi_assert_internal(last->xblock_size == 1); } } - else { - mi_assert_internal(mi_page_not_in_queue(page,tld)); + else { // free range of slices; only last slice needs a valid back offset + mi_slice_t* last = &segment->slices[maxindex]; + if (segment->kind != MI_SEGMENT_HUGE || slice->slice_count <= (segment->slice_entries - segment->segment_info_slices)) { + mi_assert_internal((uint8_t*)slice == (uint8_t*)last - last->slice_offset); + } + mi_assert_internal(slice == last || last->slice_count == 0 ); + mi_assert_internal(last->xblock_size == 0 || (segment->kind==MI_SEGMENT_HUGE && last->xblock_size==1)); + if (segment->kind != MI_SEGMENT_HUGE && segment->thread_id != 0) { // segment is not huge or abandoned + sq = mi_span_queue_for(slice->slice_count,tld); + mi_assert_internal(mi_span_queue_contains(sq,slice)); + } } + slice = &segment->slices[maxindex+1]; } + mi_assert_internal(slice == end); + mi_assert_internal(used_count == segment->used + 1); + return true; } - -static void mi_reset_delayed(mi_segments_tld_t* tld) { - if (!mi_option_is_enabled(mi_option_page_reset)) return; - mi_msecs_t now = _mi_clock_now(); - mi_page_queue_t* pq = &tld->pages_reset; - // from oldest up to the first that has not expired yet - mi_page_t* page = pq->last; - while (page != NULL && mi_page_reset_is_expired(page,now)) { - mi_page_t* const prev = page->prev; // save previous field - mi_page_reset(_mi_page_segment(page), page, 0, tld); - page->used = 0; - page->prev = page->next = NULL; - page = prev; - } - // discard the reset pages from the queue - pq->last = page; - if (page != NULL){ - page->next = NULL; - } - else { - pq->first = NULL; - } -} - +#endif /* ----------------------------------------------------------- Segment size calculations ----------------------------------------------------------- */ -// Raw start of the page available memory; can be used on uninitialized pages (only `segment_idx` must be set) -// The raw start is not taking aligned block allocation into consideration. -static uint8_t* mi_segment_raw_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size) { - size_t psize = (segment->page_kind == MI_PAGE_HUGE ? segment->segment_size : (size_t)1 << segment->page_shift); - uint8_t* p = (uint8_t*)segment + page->segment_idx * psize; - - if (page->segment_idx == 0) { - // the first page starts after the segment info (and possible guard page) - p += segment->segment_info_size; - psize -= segment->segment_info_size; - } - -#if (MI_SECURE > 1) // every page has an os guard page - psize -= _mi_os_page_size(); -#elif (MI_SECURE==1) // the last page has an os guard page at the end - if (page->segment_idx == segment->capacity - 1) { - psize -= _mi_os_page_size(); - } -#endif - - if (page_size != NULL) *page_size = psize; - mi_assert_internal(page->xblock_size == 0 || _mi_ptr_page(p) == page); - mi_assert_internal(_mi_ptr_segment(p) == segment); - return p; +static size_t mi_segment_info_size(mi_segment_t* segment) { + return segment->segment_info_slices * MI_SEGMENT_SLICE_SIZE; } -// Start of the page available memory; can be used on uninitialized pages (only `segment_idx` must be set) -uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t block_size, size_t* page_size, size_t* pre_size) +static uint8_t* _mi_segment_page_start_from_slice(const mi_segment_t* segment, const mi_slice_t* slice, size_t xblock_size, size_t* page_size) { - size_t psize; - uint8_t* p = mi_segment_raw_page_start(segment, page, &psize); - if (pre_size != NULL) *pre_size = 0; - if (page->segment_idx == 0 && block_size > 0 && segment->page_kind <= MI_PAGE_MEDIUM) { - // for small and medium objects, ensure the page start is aligned with the block size (PR#66 by kickunderscore) - size_t adjust = block_size - ((uintptr_t)p % block_size); - if (adjust < block_size) { - p += adjust; - psize -= adjust; - if (pre_size != NULL) *pre_size = adjust; - } - mi_assert_internal((uintptr_t)p % block_size == 0); - } + ptrdiff_t idx = slice - segment->slices; + size_t psize = (size_t)slice->slice_count * MI_SEGMENT_SLICE_SIZE; + // make the start not OS page aligned for smaller blocks to avoid page/cache effects + // note: the offset must always be an xblock_size multiple since we assume small allocations + // are aligned (see `mi_heap_malloc_aligned`). + size_t start_offset = 0; + if (xblock_size >= MI_INTPTR_SIZE) { + if (xblock_size <= 64) { start_offset = 3*xblock_size; } + else if (xblock_size <= 512) { start_offset = xblock_size; } + } + if (page_size != NULL) { *page_size = psize - start_offset; } + return (uint8_t*)segment + ((idx*MI_SEGMENT_SLICE_SIZE) + start_offset); +} - if (page_size != NULL) *page_size = psize; - mi_assert_internal(page->xblock_size==0 || _mi_ptr_page(p) == page); +// Start of the page available memory; can be used on uninitialized pages +uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size) +{ + const mi_slice_t* slice = mi_page_to_slice((mi_page_t*)page); + uint8_t* p = _mi_segment_page_start_from_slice(segment, slice, page->xblock_size, page_size); + mi_assert_internal(page->xblock_size > 0 || _mi_ptr_page(p) == page); mi_assert_internal(_mi_ptr_segment(p) == segment); return p; } -static size_t mi_segment_size(size_t capacity, size_t required, size_t* pre_size, size_t* info_size) -{ - const size_t minsize = sizeof(mi_segment_t) + ((capacity - 1) * sizeof(mi_page_t)) + 16 /* padding */; - size_t guardsize = 0; - size_t isize = 0; - if (MI_SECURE == 0) { - // normally no guard pages - isize = _mi_align_up(minsize, 16 * MI_MAX_ALIGN_SIZE); - } - else { +static size_t mi_segment_calculate_slices(size_t required, size_t* pre_size, size_t* info_slices) { + size_t page_size = _mi_os_page_size(); + size_t isize = _mi_align_up(sizeof(mi_segment_t), page_size); + size_t guardsize = 0; + + if (MI_SECURE>0) { // in secure mode, we set up a protected page in between the segment info // and the page data (and one at the end of the segment) - const size_t page_size = _mi_os_page_size(); - isize = _mi_align_up(minsize, page_size); guardsize = page_size; - required = _mi_align_up(required, page_size); + if (required > 0) { + required = _mi_align_up(required, MI_SEGMENT_SLICE_SIZE) + page_size; + } } - if (info_size != NULL) *info_size = isize; - if (pre_size != NULL) *pre_size = isize + guardsize; - return (required==0 ? MI_SEGMENT_SIZE : _mi_align_up( required + isize + 2*guardsize, MI_PAGE_HUGE_ALIGN) ); + if (pre_size != NULL) *pre_size = isize; + isize = _mi_align_up(isize + guardsize, MI_SEGMENT_SLICE_SIZE); + if (info_slices != NULL) *info_slices = isize / MI_SEGMENT_SLICE_SIZE; + size_t segment_size = (required==0 ? MI_SEGMENT_SIZE : _mi_align_up( required + isize + guardsize, MI_SEGMENT_SLICE_SIZE) ); + mi_assert_internal(segment_size % MI_SEGMENT_SLICE_SIZE == 0); + return (segment_size / MI_SEGMENT_SLICE_SIZE); } @@ -462,386 +376,684 @@ static void mi_segments_track_size(long segment_size, mi_segments_tld_t* tld) { if (tld->current_size > tld->peak_size) tld->peak_size = tld->current_size; } -static void mi_segment_os_free(mi_segment_t* segment, size_t segment_size, mi_segments_tld_t* tld) { +static void mi_segment_os_free(mi_segment_t* segment, mi_segments_tld_t* tld) { segment->thread_id = 0; - mi_segments_track_size(-((long)segment_size),tld); - if (MI_SECURE != 0) { - mi_assert_internal(!segment->mem_is_pinned); - mi_segment_protect(segment, false, tld->os); // ensure no more guard pages are set + _mi_segment_map_freed_at(segment); + mi_segments_track_size(-((long)mi_segment_size(segment)),tld); + if (MI_SECURE>0) { + // _mi_os_unprotect(segment, mi_segment_size(segment)); // ensure no more guard pages are set + // unprotect the guard pages; we cannot just unprotect the whole segment size as part may be decommitted + size_t os_pagesize = _mi_os_page_size(); + _mi_os_unprotect((uint8_t*)segment + mi_segment_info_size(segment) - os_pagesize, os_pagesize); + uint8_t* end = (uint8_t*)segment + mi_segment_size(segment) - os_pagesize; + _mi_os_unprotect(end, os_pagesize); + } + + // purge delayed decommits now? (no, leave it to the cache) + // mi_segment_delayed_decommit(segment,true,tld->stats); + + // _mi_os_free(segment, mi_segment_size(segment), /*segment->memid,*/ tld->stats); + const size_t size = mi_segment_size(segment); + if (size != MI_SEGMENT_SIZE || segment->mem_align_offset != 0 || segment->kind == MI_SEGMENT_HUGE || // only push regular segments on the cache + !_mi_segment_cache_push(segment, size, segment->memid, &segment->commit_mask, &segment->decommit_mask, segment->mem_is_large, segment->mem_is_pinned, tld->os)) + { + if (!segment->mem_is_pinned) { + const size_t csize = _mi_commit_mask_committed_size(&segment->commit_mask, size); + if (csize > 0) { _mi_stat_decrease(&_mi_stats_main.committed, csize); } + } + _mi_abandoned_await_readers(); // wait until safe to free + _mi_arena_free(segment, mi_segment_size(segment), segment->mem_alignment, segment->mem_align_offset, segment->memid, segment->mem_is_pinned /* pretend not committed to not double count decommits */, tld->stats); } +} + +// called by threads that are terminating +void _mi_segment_thread_collect(mi_segments_tld_t* tld) { + MI_UNUSED(tld); + // nothing to do +} - bool any_reset = false; - bool fully_committed = true; - for (size_t i = 0; i < segment->capacity; i++) { - mi_page_t* page = &segment->pages[i]; - if (!page->is_committed) { fully_committed = false; } - if (page->is_reset) { any_reset = true; } + +/* ----------------------------------------------------------- + Commit/Decommit ranges +----------------------------------------------------------- */ + +static void mi_segment_commit_mask(mi_segment_t* segment, bool conservative, uint8_t* p, size_t size, uint8_t** start_p, size_t* full_size, mi_commit_mask_t* cm) { + mi_assert_internal(_mi_ptr_segment(p + 1) == segment); + mi_assert_internal(segment->kind != MI_SEGMENT_HUGE); + mi_commit_mask_create_empty(cm); + if (size == 0 || size > MI_SEGMENT_SIZE || segment->kind == MI_SEGMENT_HUGE) return; + const size_t segstart = mi_segment_info_size(segment); + const size_t segsize = mi_segment_size(segment); + if (p >= (uint8_t*)segment + segsize) return; + + size_t pstart = (p - (uint8_t*)segment); + mi_assert_internal(pstart + size <= segsize); + + size_t start; + size_t end; + if (conservative) { + // decommit conservative + start = _mi_align_up(pstart, MI_COMMIT_SIZE); + end = _mi_align_down(pstart + size, MI_COMMIT_SIZE); + mi_assert_internal(start >= segstart); + mi_assert_internal(end <= segsize); + } + else { + // commit liberal + start = _mi_align_down(pstart, MI_MINIMAL_COMMIT_SIZE); + end = _mi_align_up(pstart + size, MI_MINIMAL_COMMIT_SIZE); + } + if (pstart >= segstart && start < segstart) { // note: the mask is also calculated for an initial commit of the info area + start = segstart; + } + if (end > segsize) { + end = segsize; } - if (any_reset && mi_option_is_enabled(mi_option_reset_decommits)) { - fully_committed = false; + + mi_assert_internal(start <= pstart && (pstart + size) <= end); + mi_assert_internal(start % MI_COMMIT_SIZE==0 && end % MI_COMMIT_SIZE == 0); + *start_p = (uint8_t*)segment + start; + *full_size = (end > start ? end - start : 0); + if (*full_size == 0) return; + + size_t bitidx = start / MI_COMMIT_SIZE; + mi_assert_internal(bitidx < MI_COMMIT_MASK_BITS); + + size_t bitcount = *full_size / MI_COMMIT_SIZE; // can be 0 + if (bitidx + bitcount > MI_COMMIT_MASK_BITS) { + _mi_warning_message("commit mask overflow: idx=%zu count=%zu start=%zx end=%zx p=0x%p size=%zu fullsize=%zu\n", bitidx, bitcount, start, end, p, size, *full_size); } - _mi_mem_free(segment, segment_size, segment->memid, fully_committed, any_reset, tld->os); + mi_assert_internal((bitidx + bitcount) <= MI_COMMIT_MASK_BITS); + mi_commit_mask_create(bitidx, bitcount, cm); } -// The thread local segment cache is limited to be at most 1/8 of the peak size of segments in use, -#define MI_SEGMENT_CACHE_FRACTION (8) +static bool mi_segment_commitx(mi_segment_t* segment, bool commit, uint8_t* p, size_t size, mi_stats_t* stats) { + mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->decommit_mask)); -// note: returned segment may be partially reset -static mi_segment_t* mi_segment_cache_pop(size_t segment_size, mi_segments_tld_t* tld) { - if (segment_size != 0 && segment_size != MI_SEGMENT_SIZE) return NULL; - mi_segment_t* segment = tld->cache; - if (segment == NULL) return NULL; - tld->cache_count--; - tld->cache = segment->next; - segment->next = NULL; - mi_assert_internal(segment->segment_size == MI_SEGMENT_SIZE); - _mi_stat_decrease(&tld->stats->segments_cache, 1); - return segment; -} + // commit liberal, but decommit conservative + uint8_t* start = NULL; + size_t full_size = 0; + mi_commit_mask_t mask; + mi_segment_commit_mask(segment, !commit/*conservative*/, p, size, &start, &full_size, &mask); + if (mi_commit_mask_is_empty(&mask) || full_size==0) return true; -static bool mi_segment_cache_full(mi_segments_tld_t* tld) -{ - // if (tld->count == 1 && tld->cache_count==0) return false; // always cache at least the final segment of a thread - size_t max_cache = mi_option_get(mi_option_segment_cache); - if (tld->cache_count < max_cache - && tld->cache_count < (1 + (tld->peak_count / MI_SEGMENT_CACHE_FRACTION)) // at least allow a 1 element cache - ) { - return false; - } - // take the opportunity to reduce the segment cache if it is too large (now) - // TODO: this never happens as we check against peak usage, should we use current usage instead? - while (tld->cache_count > max_cache) { //(1 + (tld->peak_count / MI_SEGMENT_CACHE_FRACTION))) { - mi_segment_t* segment = mi_segment_cache_pop(0,tld); - mi_assert_internal(segment != NULL); - if (segment != NULL) mi_segment_os_free(segment, segment->segment_size, tld); - } + if (commit && !mi_commit_mask_all_set(&segment->commit_mask, &mask)) { + bool is_zero = false; + mi_commit_mask_t cmask; + mi_commit_mask_create_intersect(&segment->commit_mask, &mask, &cmask); + _mi_stat_decrease(&_mi_stats_main.committed, _mi_commit_mask_committed_size(&cmask, MI_SEGMENT_SIZE)); // adjust for overlap + if (!_mi_os_commit(start,full_size,&is_zero,stats)) return false; + mi_commit_mask_set(&segment->commit_mask, &mask); + } + else if (!commit && mi_commit_mask_any_set(&segment->commit_mask, &mask)) { + mi_assert_internal((void*)start != (void*)segment); + //mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &mask)); + + mi_commit_mask_t cmask; + mi_commit_mask_create_intersect(&segment->commit_mask, &mask, &cmask); + _mi_stat_increase(&_mi_stats_main.committed, full_size - _mi_commit_mask_committed_size(&cmask, MI_SEGMENT_SIZE)); // adjust for overlap + if (segment->allow_decommit) { + _mi_os_decommit(start, full_size, stats); // ok if this fails + } + mi_commit_mask_clear(&segment->commit_mask, &mask); + } + // increase expiration of reusing part of the delayed decommit + if (commit && mi_commit_mask_any_set(&segment->decommit_mask, &mask)) { + segment->decommit_expire = _mi_clock_now() + mi_option_get(mi_option_decommit_delay); + } + // always undo delayed decommits + mi_commit_mask_clear(&segment->decommit_mask, &mask); return true; } -static bool mi_segment_cache_push(mi_segment_t* segment, mi_segments_tld_t* tld) { - mi_assert_internal(!mi_segment_is_in_free_queue(segment, tld)); - mi_assert_internal(segment->next == NULL); - if (segment->segment_size != MI_SEGMENT_SIZE || mi_segment_cache_full(tld)) { - return false; - } - mi_assert_internal(segment->segment_size == MI_SEGMENT_SIZE); - segment->next = tld->cache; - tld->cache = segment; - tld->cache_count++; - _mi_stat_increase(&tld->stats->segments_cache,1); - return true; +static bool mi_segment_ensure_committed(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) { + mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->decommit_mask)); + // note: assumes commit_mask is always full for huge segments as otherwise the commit mask bits can overflow + if (mi_commit_mask_is_full(&segment->commit_mask) && mi_commit_mask_is_empty(&segment->decommit_mask)) return true; // fully committed + mi_assert_internal(segment->kind != MI_SEGMENT_HUGE); + return mi_segment_commitx(segment,true,p,size,stats); } -// called by threads that are terminating to free cached segments -void _mi_segment_thread_collect(mi_segments_tld_t* tld) { - mi_segment_t* segment; - while ((segment = mi_segment_cache_pop(0,tld)) != NULL) { - mi_segment_os_free(segment, segment->segment_size, tld); +static void mi_segment_perhaps_decommit(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) { + if (!segment->allow_decommit) return; + if (mi_option_get(mi_option_decommit_delay) == 0) { + mi_segment_commitx(segment, false, p, size, stats); } - mi_assert_internal(tld->cache_count == 0); - mi_assert_internal(tld->cache == NULL); -#if MI_DEBUG>=2 - if (!_mi_is_main_thread()) { - mi_assert_internal(tld->pages_reset.first == NULL); - mi_assert_internal(tld->pages_reset.last == NULL); + else { + // register for future decommit in the decommit mask + uint8_t* start = NULL; + size_t full_size = 0; + mi_commit_mask_t mask; + mi_segment_commit_mask(segment, true /*conservative*/, p, size, &start, &full_size, &mask); + if (mi_commit_mask_is_empty(&mask) || full_size==0) return; + + // update delayed commit + mi_assert_internal(segment->decommit_expire > 0 || mi_commit_mask_is_empty(&segment->decommit_mask)); + mi_commit_mask_t cmask; + mi_commit_mask_create_intersect(&segment->commit_mask, &mask, &cmask); // only decommit what is committed; span_free may try to decommit more + mi_commit_mask_set(&segment->decommit_mask, &cmask); + mi_msecs_t now = _mi_clock_now(); + if (segment->decommit_expire == 0) { + // no previous decommits, initialize now + segment->decommit_expire = now + mi_option_get(mi_option_decommit_delay); + } + else if (segment->decommit_expire <= now) { + // previous decommit mask already expired + if (segment->decommit_expire + mi_option_get(mi_option_decommit_extend_delay) <= now) { + mi_segment_delayed_decommit(segment, true, stats); + } + else { + segment->decommit_expire = now + mi_option_get(mi_option_decommit_extend_delay); // (mi_option_get(mi_option_decommit_delay) / 8); // wait a tiny bit longer in case there is a series of free's + } + } + else { + // previous decommit mask is not yet expired, increase the expiration by a bit. + segment->decommit_expire += mi_option_get(mi_option_decommit_extend_delay); + } + } +} + +static void mi_segment_delayed_decommit(mi_segment_t* segment, bool force, mi_stats_t* stats) { + if (!segment->allow_decommit || mi_commit_mask_is_empty(&segment->decommit_mask)) return; + mi_msecs_t now = _mi_clock_now(); + if (!force && now < segment->decommit_expire) return; + + mi_commit_mask_t mask = segment->decommit_mask; + segment->decommit_expire = 0; + mi_commit_mask_create_empty(&segment->decommit_mask); + + size_t idx; + size_t count; + mi_commit_mask_foreach(&mask, idx, count) { + // if found, decommit that sequence + if (count > 0) { + uint8_t* p = (uint8_t*)segment + (idx*MI_COMMIT_SIZE); + size_t size = count * MI_COMMIT_SIZE; + mi_segment_commitx(segment, false, p, size, stats); + } } -#endif + mi_commit_mask_foreach_end() + mi_assert_internal(mi_commit_mask_is_empty(&segment->decommit_mask)); } /* ----------------------------------------------------------- - Segment allocation + Span free ----------------------------------------------------------- */ -// Allocate a segment from the OS aligned to `MI_SEGMENT_SIZE` . -static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_page_kind_t page_kind, size_t page_shift, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) -{ - // the segment parameter is non-null if it came from our cache - mi_assert_internal(segment==NULL || (required==0 && page_kind <= MI_PAGE_LARGE)); +static bool mi_segment_is_abandoned(mi_segment_t* segment) { + return (segment->thread_id == 0); +} - // calculate needed sizes first - size_t capacity; - if (page_kind == MI_PAGE_HUGE) { - mi_assert_internal(page_shift == MI_SEGMENT_SHIFT && required > 0); - capacity = 1; - } - else { - mi_assert_internal(required == 0); - size_t page_size = (size_t)1 << page_shift; - capacity = MI_SEGMENT_SIZE / page_size; - mi_assert_internal(MI_SEGMENT_SIZE % page_size == 0); - mi_assert_internal(capacity >= 1 && capacity <= MI_SMALL_PAGES_PER_SEGMENT); - } - size_t info_size; - size_t pre_size; - size_t segment_size = mi_segment_size(capacity, required, &pre_size, &info_size); - mi_assert_internal(segment_size >= required); +// note: can be called on abandoned segments +static void mi_segment_span_free(mi_segment_t* segment, size_t slice_index, size_t slice_count, bool allow_decommit, mi_segments_tld_t* tld) { + mi_assert_internal(slice_index < segment->slice_entries); + mi_span_queue_t* sq = (segment->kind == MI_SEGMENT_HUGE || mi_segment_is_abandoned(segment) + ? NULL : mi_span_queue_for(slice_count,tld)); + if (slice_count==0) slice_count = 1; + mi_assert_internal(slice_index + slice_count - 1 < segment->slice_entries); + + // set first and last slice (the intermediates can be undetermined) + mi_slice_t* slice = &segment->slices[slice_index]; + slice->slice_count = (uint32_t)slice_count; + mi_assert_internal(slice->slice_count == slice_count); // no overflow? + slice->slice_offset = 0; + if (slice_count > 1) { + mi_slice_t* last = &segment->slices[slice_index + slice_count - 1]; + last->slice_count = 0; + last->slice_offset = (uint32_t)(sizeof(mi_page_t)*(slice_count - 1)); + last->xblock_size = 0; + } + + // perhaps decommit + if (allow_decommit) { + mi_segment_perhaps_decommit(segment, mi_slice_start(slice), slice_count * MI_SEGMENT_SLICE_SIZE, tld->stats); + } + + // and push it on the free page queue (if it was not a huge page) + if (sq != NULL) mi_span_queue_push( sq, slice ); + else slice->xblock_size = 0; // mark huge page as free anyways +} - // Initialize parameters - const bool eager_delayed = (page_kind <= MI_PAGE_MEDIUM && tld->count < (size_t)mi_option_get(mi_option_eager_commit_delay)); - const bool eager = !eager_delayed && mi_option_is_enabled(mi_option_eager_commit); - bool commit = eager; // || (page_kind >= MI_PAGE_LARGE); - bool pages_still_good = false; - bool is_zero = false; +/* +// called from reclaim to add existing free spans +static void mi_segment_span_add_free(mi_slice_t* slice, mi_segments_tld_t* tld) { + mi_segment_t* segment = _mi_ptr_segment(slice); + mi_assert_internal(slice->xblock_size==0 && slice->slice_count>0 && slice->slice_offset==0); + size_t slice_index = mi_slice_index(slice); + mi_segment_span_free(segment,slice_index,slice->slice_count,tld); +} +*/ - // Try to get it from our thread local cache first - if (segment != NULL) { - // came from cache - mi_assert_internal(segment->segment_size == segment_size); - if (page_kind <= MI_PAGE_MEDIUM && segment->page_kind == page_kind && segment->segment_size == segment_size) { - pages_still_good = true; +static void mi_segment_span_remove_from_queue(mi_slice_t* slice, mi_segments_tld_t* tld) { + mi_assert_internal(slice->slice_count > 0 && slice->slice_offset==0 && slice->xblock_size==0); + mi_assert_internal(_mi_ptr_segment(slice)->kind != MI_SEGMENT_HUGE); + mi_span_queue_t* sq = mi_span_queue_for(slice->slice_count, tld); + mi_span_queue_delete(sq, slice); +} + +// note: can be called on abandoned segments +static mi_slice_t* mi_segment_span_free_coalesce(mi_slice_t* slice, mi_segments_tld_t* tld) { + mi_assert_internal(slice != NULL && slice->slice_count > 0 && slice->slice_offset == 0); + mi_segment_t* segment = _mi_ptr_segment(slice); + bool is_abandoned = mi_segment_is_abandoned(segment); + + // for huge pages, just mark as free but don't add to the queues + if (segment->kind == MI_SEGMENT_HUGE) { + // issue #691: segment->used can be 0 if the huge page block was freed while abandoned (reclaim will get here in that case) + mi_assert_internal((segment->used==0 && slice->xblock_size==0) || segment->used == 1); // decreased right after this call in `mi_segment_page_clear` + slice->xblock_size = 0; // mark as free anyways + // we should mark the last slice `xblock_size=0` now to maintain invariants but we skip it to + // avoid a possible cache miss (and the segment is about to be freed) + return slice; + } + + // otherwise coalesce the span and add to the free span queues + size_t slice_count = slice->slice_count; + mi_slice_t* next = slice + slice->slice_count; + mi_assert_internal(next <= mi_segment_slices_end(segment)); + if (next < mi_segment_slices_end(segment) && next->xblock_size==0) { + // free next block -- remove it from free and merge + mi_assert_internal(next->slice_count > 0 && next->slice_offset==0); + slice_count += next->slice_count; // extend + if (!is_abandoned) { mi_segment_span_remove_from_queue(next, tld); } + } + if (slice > segment->slices) { + mi_slice_t* prev = mi_slice_first(slice - 1); + mi_assert_internal(prev >= segment->slices); + if (prev->xblock_size==0) { + // free previous slice -- remove it from free and merge + mi_assert_internal(prev->slice_count > 0 && prev->slice_offset==0); + slice_count += prev->slice_count; + if (!is_abandoned) { mi_segment_span_remove_from_queue(prev, tld); } + slice = prev; } - else - { - if (MI_SECURE!=0) { - mi_assert_internal(!segment->mem_is_pinned); - mi_segment_protect(segment, false, tld->os); // reset protection if the page kind differs - } - // different page kinds; unreset any reset pages, and unprotect - // TODO: optimize cache pop to return fitting pages if possible? - for (size_t i = 0; i < segment->capacity; i++) { - mi_page_t* page = &segment->pages[i]; - if (page->is_reset) { - if (!commit && mi_option_is_enabled(mi_option_reset_decommits)) { - page->is_reset = false; + } + + // and add the new free page + mi_segment_span_free(segment, mi_slice_index(slice), slice_count, true, tld); + return slice; +} + + + +/* ----------------------------------------------------------- + Page allocation +----------------------------------------------------------- */ + +// Note: may still return NULL if committing the memory failed +static mi_page_t* mi_segment_span_allocate(mi_segment_t* segment, size_t slice_index, size_t slice_count, mi_segments_tld_t* tld) { + mi_assert_internal(slice_index < segment->slice_entries); + mi_slice_t* const slice = &segment->slices[slice_index]; + mi_assert_internal(slice->xblock_size==0 || slice->xblock_size==1); + + // commit before changing the slice data + if (!mi_segment_ensure_committed(segment, _mi_segment_page_start_from_slice(segment, slice, 0, NULL), slice_count * MI_SEGMENT_SLICE_SIZE, tld->stats)) { + return NULL; // commit failed! + } + + // convert the slices to a page + slice->slice_offset = 0; + slice->slice_count = (uint32_t)slice_count; + mi_assert_internal(slice->slice_count == slice_count); + const size_t bsize = slice_count * MI_SEGMENT_SLICE_SIZE; + slice->xblock_size = (uint32_t)(bsize >= MI_HUGE_BLOCK_SIZE ? MI_HUGE_BLOCK_SIZE : bsize); + mi_page_t* page = mi_slice_to_page(slice); + mi_assert_internal(mi_page_block_size(page) == bsize); + + // set slice back pointers for the first MI_MAX_SLICE_OFFSET entries + size_t extra = slice_count-1; + if (extra > MI_MAX_SLICE_OFFSET) extra = MI_MAX_SLICE_OFFSET; + if (slice_index + extra >= segment->slice_entries) extra = segment->slice_entries - slice_index - 1; // huge objects may have more slices than avaiable entries in the segment->slices + + mi_slice_t* slice_next = slice + 1; + for (size_t i = 1; i <= extra; i++, slice_next++) { + slice_next->slice_offset = (uint32_t)(sizeof(mi_slice_t)*i); + slice_next->slice_count = 0; + slice_next->xblock_size = 1; + } + + // and also for the last one (if not set already) (the last one is needed for coalescing and for large alignments) + // note: the cast is needed for ubsan since the index can be larger than MI_SLICES_PER_SEGMENT for huge allocations (see #543) + mi_slice_t* last = slice + slice_count - 1; + mi_slice_t* end = (mi_slice_t*)mi_segment_slices_end(segment); + if (last > end) last = end; + if (last > slice) { + last->slice_offset = (uint32_t)(sizeof(mi_slice_t) * (last - slice)); + last->slice_count = 0; + last->xblock_size = 1; + } + + // and initialize the page + page->is_reset = false; + page->is_committed = true; + segment->used++; + return page; +} + +static void mi_segment_slice_split(mi_segment_t* segment, mi_slice_t* slice, size_t slice_count, mi_segments_tld_t* tld) { + mi_assert_internal(_mi_ptr_segment(slice) == segment); + mi_assert_internal(slice->slice_count >= slice_count); + mi_assert_internal(slice->xblock_size > 0); // no more in free queue + if (slice->slice_count <= slice_count) return; + mi_assert_internal(segment->kind != MI_SEGMENT_HUGE); + size_t next_index = mi_slice_index(slice) + slice_count; + size_t next_count = slice->slice_count - slice_count; + mi_segment_span_free(segment, next_index, next_count, false /* don't decommit left-over part */, tld); + slice->slice_count = (uint32_t)slice_count; +} + +static mi_page_t* mi_segments_page_find_and_allocate(size_t slice_count, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld) { + mi_assert_internal(slice_count*MI_SEGMENT_SLICE_SIZE <= MI_LARGE_OBJ_SIZE_MAX); + // search from best fit up + mi_span_queue_t* sq = mi_span_queue_for(slice_count, tld); + if (slice_count == 0) slice_count = 1; + while (sq <= &tld->spans[MI_SEGMENT_BIN_MAX]) { + for (mi_slice_t* slice = sq->first; slice != NULL; slice = slice->next) { + if (slice->slice_count >= slice_count) { + // found one + mi_segment_t* segment = _mi_ptr_segment(slice); + if (_mi_arena_memid_is_suitable(segment->memid, req_arena_id)) { + // found a suitable page span + mi_span_queue_delete(sq, slice); + + if (slice->slice_count > slice_count) { + mi_segment_slice_split(segment, slice, slice_count, tld); } - else { - mi_page_unreset(segment, page, 0, tld); // todo: only unreset the part that was reset? (instead of the full page) + mi_assert_internal(slice != NULL && slice->slice_count == slice_count && slice->xblock_size > 0); + mi_page_t* page = mi_segment_span_allocate(segment, mi_slice_index(slice), slice->slice_count, tld); + if (page == NULL) { + // commit failed; return NULL but first restore the slice + mi_segment_span_free_coalesce(slice, tld); + return NULL; } - } - } - // ensure the initial info is committed - if (segment->capacity < capacity) { - bool commit_zero = false; - bool ok = _mi_mem_commit(segment, pre_size, &commit_zero, tld->os); - if (commit_zero) is_zero = true; - if (!ok) { - return NULL; + return page; } } } + sq++; } - else { - // Allocate the segment from the OS - size_t memid; - bool mem_large = (!eager_delayed && (MI_SECURE==0)); // only allow large OS pages once we are no longer lazy - bool is_pinned = false; - segment = (mi_segment_t*)_mi_mem_alloc_aligned(segment_size, MI_SEGMENT_SIZE, &commit, &mem_large, &is_pinned, &is_zero, &memid, os_tld); + // could not find a page.. + return NULL; +} + + +/* ----------------------------------------------------------- + Segment allocation +----------------------------------------------------------- */ + +static mi_segment_t* mi_segment_os_alloc( size_t required, size_t page_alignment, bool eager_delay, mi_arena_id_t req_arena_id, + size_t* psegment_slices, size_t* ppre_size, size_t* pinfo_slices, + mi_commit_mask_t* pcommit_mask, mi_commit_mask_t* pdecommit_mask, + bool* is_zero, bool* pcommit, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) + +{ + // Allocate the segment from the OS + bool mem_large = (!eager_delay && (MI_SECURE==0)); // only allow large OS pages once we are no longer lazy + bool is_pinned = false; + size_t memid = 0; + size_t align_offset = 0; + size_t alignment = MI_SEGMENT_ALIGN; + + if (page_alignment > 0) { + // mi_assert_internal(huge_page != NULL); + mi_assert_internal(page_alignment >= MI_SEGMENT_ALIGN); + alignment = page_alignment; + const size_t info_size = (*pinfo_slices) * MI_SEGMENT_SLICE_SIZE; + align_offset = _mi_align_up( info_size, MI_SEGMENT_ALIGN ); + const size_t extra = align_offset - info_size; + // recalculate due to potential guard pages + *psegment_slices = mi_segment_calculate_slices(required + extra, ppre_size, pinfo_slices); + } + const size_t segment_size = (*psegment_slices) * MI_SEGMENT_SLICE_SIZE; + mi_segment_t* segment = NULL; + + // get from cache? + if (page_alignment == 0) { + segment = (mi_segment_t*)_mi_segment_cache_pop(segment_size, pcommit_mask, pdecommit_mask, mem_large, &mem_large, &is_pinned, is_zero, req_arena_id, &memid, os_tld); + } + + // get from OS + if (segment==NULL) { + segment = (mi_segment_t*)_mi_arena_alloc_aligned(segment_size, alignment, align_offset, pcommit, &mem_large, &is_pinned, is_zero, req_arena_id, &memid, os_tld); if (segment == NULL) return NULL; // failed to allocate - if (!commit) { - // ensure the initial info is committed - mi_assert_internal(!mem_large && !is_pinned); - bool commit_zero = false; - bool ok = _mi_mem_commit(segment, pre_size, &commit_zero, tld->os); - if (commit_zero) is_zero = true; - if (!ok) { - // commit failed; we cannot touch the memory: free the segment directly and return `NULL` - _mi_mem_free(segment, MI_SEGMENT_SIZE, memid, false, false, os_tld); - return NULL; - } + if (*pcommit) { + mi_commit_mask_create_full(pcommit_mask); } - segment->memid = memid; - segment->mem_is_pinned = (mem_large || is_pinned); - segment->mem_is_committed = commit; - mi_segments_track_size((long)segment_size, tld); - } + else { + mi_commit_mask_create_empty(pcommit_mask); + } + } mi_assert_internal(segment != NULL && (uintptr_t)segment % MI_SEGMENT_SIZE == 0); - mi_assert_internal(segment->mem_is_pinned ? segment->mem_is_committed : true); + + const size_t commit_needed = _mi_divide_up((*pinfo_slices)*MI_SEGMENT_SLICE_SIZE, MI_COMMIT_SIZE); + mi_assert_internal(commit_needed>0); + mi_commit_mask_t commit_needed_mask; + mi_commit_mask_create(0, commit_needed, &commit_needed_mask); + if (!mi_commit_mask_all_set(pcommit_mask, &commit_needed_mask)) { + // at least commit the info slices + mi_assert_internal(commit_needed*MI_COMMIT_SIZE >= (*pinfo_slices)*MI_SEGMENT_SLICE_SIZE); + bool ok = _mi_os_commit(segment, commit_needed*MI_COMMIT_SIZE, is_zero, tld->stats); + if (!ok) return NULL; // failed to commit + mi_commit_mask_set(pcommit_mask, &commit_needed_mask); + } + else if (*is_zero) { + // track zero initialization for valgrind + mi_track_mem_defined(segment, commit_needed * MI_COMMIT_SIZE); + } + segment->memid = memid; + segment->mem_is_pinned = is_pinned; + segment->mem_is_large = mem_large; + segment->mem_is_committed = mi_commit_mask_is_full(pcommit_mask); + segment->mem_alignment = alignment; + segment->mem_align_offset = align_offset; + mi_segments_track_size((long)(segment_size), tld); + _mi_segment_map_allocated_at(segment); + return segment; +} + + +// Allocate a segment from the OS aligned to `MI_SEGMENT_SIZE` . +static mi_segment_t* mi_segment_alloc(size_t required, size_t page_alignment, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld, mi_os_tld_t* os_tld, mi_page_t** huge_page) +{ + mi_assert_internal((required==0 && huge_page==NULL) || (required>0 && huge_page != NULL)); + + // calculate needed sizes first + size_t info_slices; + size_t pre_size; + size_t segment_slices = mi_segment_calculate_slices(required, &pre_size, &info_slices); + + // Commit eagerly only if not the first N lazy segments (to reduce impact of many threads that allocate just a little) + const bool eager_delay = (// !_mi_os_has_overcommit() && // never delay on overcommit systems + _mi_current_thread_count() > 1 && // do not delay for the first N threads + tld->count < (size_t)mi_option_get(mi_option_eager_commit_delay)); + const bool eager = !eager_delay && mi_option_is_enabled(mi_option_eager_commit); + bool commit = eager || (required > 0); + bool is_zero = false; + + mi_commit_mask_t commit_mask; + mi_commit_mask_t decommit_mask; + mi_commit_mask_create_empty(&commit_mask); + mi_commit_mask_create_empty(&decommit_mask); + + // Allocate the segment from the OS + mi_segment_t* segment = mi_segment_os_alloc(required, page_alignment, eager_delay, req_arena_id, + &segment_slices, &pre_size, &info_slices, &commit_mask, &decommit_mask, + &is_zero, &commit, tld, os_tld); + if (segment == NULL) return NULL; + + // zero the segment info? -- not always needed as it may be zero initialized from the OS mi_atomic_store_ptr_release(mi_segment_t, &segment->abandoned_next, NULL); // tsan - if (!pages_still_good) { - // zero the segment info (but not the `mem` fields) - ptrdiff_t ofs = offsetof(mi_segment_t, next); - memset((uint8_t*)segment + ofs, 0, info_size - ofs); - - // initialize pages info - for (uint8_t i = 0; i < capacity; i++) { - segment->pages[i].segment_idx = i; - segment->pages[i].is_reset = false; - segment->pages[i].is_committed = commit; - segment->pages[i].is_zero_init = is_zero; - } - } + { + ptrdiff_t ofs = offsetof(mi_segment_t, next); + size_t prefix = offsetof(mi_segment_t, slices) - ofs; + size_t zsize = prefix + (sizeof(mi_slice_t) * (segment_slices + 1)); // one more + if (!is_zero) { + memset((uint8_t*)segment + ofs, 0, zsize); + } + } + + segment->commit_mask = commit_mask; // on lazy commit, the initial part is always committed + segment->allow_decommit = (mi_option_is_enabled(mi_option_allow_decommit) && !segment->mem_is_pinned && !segment->mem_is_large); + if (segment->allow_decommit) { + segment->decommit_expire = 0; // don't decommit just committed memory // _mi_clock_now() + mi_option_get(mi_option_decommit_delay); + segment->decommit_mask = decommit_mask; + mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->decommit_mask)); + #if MI_DEBUG>2 + const size_t commit_needed = _mi_divide_up(info_slices*MI_SEGMENT_SLICE_SIZE, MI_COMMIT_SIZE); + mi_commit_mask_t commit_needed_mask; + mi_commit_mask_create(0, commit_needed, &commit_needed_mask); + mi_assert_internal(!mi_commit_mask_any_set(&segment->decommit_mask, &commit_needed_mask)); + #endif + } else { - // zero the segment info but not the pages info (and mem fields) - ptrdiff_t ofs = offsetof(mi_segment_t, next); - memset((uint8_t*)segment + ofs, 0, offsetof(mi_segment_t,pages) - ofs); - } - - // initialize - segment->page_kind = page_kind; - segment->capacity = capacity; - segment->page_shift = page_shift; - segment->segment_size = segment_size; - segment->segment_info_size = pre_size; - segment->thread_id = _mi_thread_id(); + segment->decommit_expire = 0; + mi_commit_mask_create_empty( &segment->decommit_mask ); + } + + // initialize segment info + const size_t slice_entries = (segment_slices > MI_SLICES_PER_SEGMENT ? MI_SLICES_PER_SEGMENT : segment_slices); + segment->segment_slices = segment_slices; + segment->segment_info_slices = info_slices; + segment->thread_id = _mi_thread_id(); segment->cookie = _mi_ptr_cookie(segment); - // _mi_stat_increase(&tld->stats->page_committed, segment->segment_info_size); + segment->slice_entries = slice_entries; + segment->kind = (required == 0 ? MI_SEGMENT_NORMAL : MI_SEGMENT_HUGE); - // set protection - mi_segment_protect(segment, true, tld->os); + // memset(segment->slices, 0, sizeof(mi_slice_t)*(info_slices+1)); + _mi_stat_increase(&tld->stats->page_committed, mi_segment_info_size(segment)); - // insert in free lists for small and medium pages - if (page_kind <= MI_PAGE_MEDIUM) { - mi_segment_insert_in_free_queue(segment, tld); + // set up guard pages + size_t guard_slices = 0; + if (MI_SECURE>0) { + // in secure mode, we set up a protected page in between the segment info + // and the page data, and at the end of the segment. + size_t os_pagesize = _mi_os_page_size(); + mi_assert_internal(mi_segment_info_size(segment) - os_pagesize >= pre_size); + _mi_os_protect((uint8_t*)segment + mi_segment_info_size(segment) - os_pagesize, os_pagesize); + uint8_t* end = (uint8_t*)segment + mi_segment_size(segment) - os_pagesize; + mi_segment_ensure_committed(segment, end, os_pagesize, tld->stats); + _mi_os_protect(end, os_pagesize); + if (slice_entries == segment_slices) segment->slice_entries--; // don't use the last slice :-( + guard_slices = 1; + } + + // reserve first slices for segment info + mi_page_t* page0 = mi_segment_span_allocate(segment, 0, info_slices, tld); + mi_assert_internal(page0!=NULL); if (page0==NULL) return NULL; // cannot fail as we always commit in advance + mi_assert_internal(segment->used == 1); + segment->used = 0; // don't count our internal slices towards usage + + // initialize initial free pages + if (segment->kind == MI_SEGMENT_NORMAL) { // not a huge page + mi_assert_internal(huge_page==NULL); + mi_segment_span_free(segment, info_slices, segment->slice_entries - info_slices, false /* don't decommit */, tld); + } + else { + mi_assert_internal(huge_page!=NULL); + mi_assert_internal(mi_commit_mask_is_empty(&segment->decommit_mask)); + mi_assert_internal(mi_commit_mask_is_full(&segment->commit_mask)); + *huge_page = mi_segment_span_allocate(segment, info_slices, segment_slices - info_slices - guard_slices, tld); + mi_assert_internal(*huge_page != NULL); // cannot fail as we commit in advance } - //fprintf(stderr,"mimalloc: alloc segment at %p\n", (void*)segment); + mi_assert_expensive(mi_segment_is_valid(segment,tld)); return segment; } -static mi_segment_t* mi_segment_alloc(size_t required, mi_page_kind_t page_kind, size_t page_shift, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { - return mi_segment_init(NULL, required, page_kind, page_shift, tld, os_tld); -} static void mi_segment_free(mi_segment_t* segment, bool force, mi_segments_tld_t* tld) { - UNUSED(force); - mi_assert(segment != NULL); - // note: don't reset pages even on abandon as the whole segment is freed? (and ready for reuse) - bool force_reset = (force && mi_option_is_enabled(mi_option_abandoned_page_reset)); - mi_pages_reset_remove_all_in_segment(segment, force_reset, tld); - mi_segment_remove_from_free_queue(segment,tld); - - mi_assert_expensive(!mi_segment_queue_contains(&tld->small_free, segment)); - mi_assert_expensive(!mi_segment_queue_contains(&tld->medium_free, segment)); - mi_assert(segment->next == NULL); - mi_assert(segment->prev == NULL); - _mi_stat_decrease(&tld->stats->page_committed, segment->segment_info_size); - - if (!force && mi_segment_cache_push(segment, tld)) { - // it is put in our cache - } - else { - // otherwise return it to the OS - mi_segment_os_free(segment, segment->segment_size, tld); + MI_UNUSED(force); + mi_assert_internal(segment != NULL); + mi_assert_internal(segment->next == NULL); + mi_assert_internal(segment->used == 0); + + // Remove the free pages + mi_slice_t* slice = &segment->slices[0]; + const mi_slice_t* end = mi_segment_slices_end(segment); + #if MI_DEBUG>1 + size_t page_count = 0; + #endif + while (slice < end) { + mi_assert_internal(slice->slice_count > 0); + mi_assert_internal(slice->slice_offset == 0); + mi_assert_internal(mi_slice_index(slice)==0 || slice->xblock_size == 0); // no more used pages .. + if (slice->xblock_size == 0 && segment->kind != MI_SEGMENT_HUGE) { + mi_segment_span_remove_from_queue(slice, tld); + } + #if MI_DEBUG>1 + page_count++; + #endif + slice = slice + slice->slice_count; } -} - -/* ----------------------------------------------------------- - Free page management inside a segment ------------------------------------------------------------ */ + mi_assert_internal(page_count == 2); // first page is allocated by the segment itself + // stats + _mi_stat_decrease(&tld->stats->page_committed, mi_segment_info_size(segment)); -static bool mi_segment_has_free(const mi_segment_t* segment) { - return (segment->used < segment->capacity); -} - -static bool mi_segment_page_claim(mi_segment_t* segment, mi_page_t* page, mi_segments_tld_t* tld) { - mi_assert_internal(_mi_page_segment(page) == segment); - mi_assert_internal(!page->segment_in_use); - mi_pages_reset_remove(page, tld); - // check commit - if (!page->is_committed) { - mi_assert_internal(!segment->mem_is_pinned); - mi_assert_internal(!page->is_reset); - size_t psize; - uint8_t* start = mi_segment_raw_page_start(segment, page, &psize); - bool is_zero = false; - const size_t gsize = (MI_SECURE >= 2 ? _mi_os_page_size() : 0); - bool ok = _mi_mem_commit(start, psize + gsize, &is_zero, tld->os); - if (!ok) return false; // failed to commit! - if (gsize > 0) { mi_segment_protect_range(start + psize, gsize, true); } - if (is_zero) { page->is_zero_init = true; } - page->is_committed = true; - } - // set in-use before doing unreset to prevent delayed reset - page->segment_in_use = true; - segment->used++; - // check reset - if (page->is_reset) { - mi_assert_internal(!segment->mem_is_pinned); - bool ok = mi_page_unreset(segment, page, 0, tld); - if (!ok) { - page->segment_in_use = false; - segment->used--; - return false; - } - } - mi_assert_internal(page->segment_in_use); - mi_assert_internal(segment->used <= segment->capacity); - if (segment->used == segment->capacity && segment->page_kind <= MI_PAGE_MEDIUM) { - // if no more free pages, remove from the queue - mi_assert_internal(!mi_segment_has_free(segment)); - mi_segment_remove_from_free_queue(segment, tld); - } - return true; + // return it to the OS + mi_segment_os_free(segment, tld); } /* ----------------------------------------------------------- - Free + Page Free ----------------------------------------------------------- */ static void mi_segment_abandon(mi_segment_t* segment, mi_segments_tld_t* tld); -// clear page data; can be called on abandoned segments -static void mi_segment_page_clear(mi_segment_t* segment, mi_page_t* page, bool allow_reset, mi_segments_tld_t* tld) -{ - mi_assert_internal(page->segment_in_use); +// note: can be called on abandoned pages +static mi_slice_t* mi_segment_page_clear(mi_page_t* page, mi_segments_tld_t* tld) { + mi_assert_internal(page->xblock_size > 0); mi_assert_internal(mi_page_all_free(page)); - mi_assert_internal(page->is_committed); - mi_assert_internal(mi_page_not_in_queue(page, tld)); - + mi_segment_t* segment = _mi_ptr_segment(page); + mi_assert_internal(segment->used > 0); + size_t inuse = page->capacity * mi_page_block_size(page); _mi_stat_decrease(&tld->stats->page_committed, inuse); _mi_stat_decrease(&tld->stats->pages, 1); - // calculate the used size from the raw (non-aligned) start of the page - //size_t pre_size; - //_mi_segment_page_start(segment, page, page->block_size, NULL, &pre_size); - //size_t used_size = pre_size + (page->capacity * page->block_size); + // reset the page memory to reduce memory pressure? + if (!segment->mem_is_pinned && !page->is_reset && mi_option_is_enabled(mi_option_page_reset)) { + size_t psize; + uint8_t* start = _mi_page_start(segment, page, &psize); + page->is_reset = true; + _mi_os_reset(start, psize, tld->stats); + } + // zero the page data, but not the segment fields page->is_zero_init = false; - page->segment_in_use = false; - - // reset the page memory to reduce memory pressure? - // note: must come after setting `segment_in_use` to false but before block_size becomes 0 - //mi_page_reset(segment, page, 0 /*used_size*/, tld); - - // zero the page data, but not the segment fields and capacity, and block_size (for page size calculations) - uint32_t block_size = page->xblock_size; - uint16_t capacity = page->capacity; - uint16_t reserved = page->reserved; - ptrdiff_t ofs = offsetof(mi_page_t,capacity); + ptrdiff_t ofs = offsetof(mi_page_t, capacity); memset((uint8_t*)page + ofs, 0, sizeof(*page) - ofs); - page->capacity = capacity; - page->reserved = reserved; - page->xblock_size = block_size; - segment->used--; - - // add to the free page list for reuse/reset - if (allow_reset) { - mi_pages_reset_add(segment, page, tld); - } + page->xblock_size = 1; - page->capacity = 0; // after reset these can be zero'd now - page->reserved = 0; + // and free it + mi_slice_t* slice = mi_segment_span_free_coalesce(mi_page_to_slice(page), tld); + segment->used--; + // cannot assert segment valid as it is called during reclaim + // mi_assert_expensive(mi_segment_is_valid(segment, tld)); + return slice; } void _mi_segment_page_free(mi_page_t* page, bool force, mi_segments_tld_t* tld) { mi_assert(page != NULL); + mi_segment_t* segment = _mi_page_segment(page); mi_assert_expensive(mi_segment_is_valid(segment,tld)); - mi_reset_delayed(tld); // mark it as free now - mi_segment_page_clear(segment, page, true, tld); + mi_segment_page_clear(page, tld); + mi_assert_expensive(mi_segment_is_valid(segment, tld)); if (segment->used == 0) { // no more used pages; remove from the free list and free the segment mi_segment_free(segment, force, tld); } - else { - if (segment->used == segment->abandoned) { - // only abandoned pages; remove from free list and abandon - mi_segment_abandon(segment,tld); - } - else if (segment->used + 1 == segment->capacity) { - mi_assert_internal(segment->page_kind <= MI_PAGE_MEDIUM); // for now we only support small and medium pages - // move back to segments free list - mi_segment_insert_in_free_queue(segment,tld); - } + else if (segment->used == segment->abandoned) { + // only abandoned pages; remove from free list and abandon + mi_segment_abandon(segment,tld); } } @@ -850,7 +1062,7 @@ void _mi_segment_page_free(mi_page_t* page, bool force, mi_segments_tld_t* tld) Abandonment When threads terminate, they can leave segments with -live blocks (reached through other threads). Such segments +live blocks (reachable through other threads). Such segments are "abandoned" and will be reclaimed by other threads to reuse their pages and/or free them eventually @@ -858,18 +1070,18 @@ We maintain a global list of abandoned segments that are reclaimed on demand. Since this is shared among threads the implementation needs to avoid the A-B-A problem on popping abandoned segments: -We use tagged pointers to avoid accidentially identifying +We use tagged pointers to avoid accidentally identifying reused segments, much like stamped references in Java. Secondly, we maintain a reader counter to avoid resetting or decommitting segments that have a pending read operation. Note: the current implementation is one possible design; another way might be to keep track of abandoned segments -in the regions. This would have the advantage of keeping +in the arenas/segment_cache's. This would have the advantage of keeping all concurrent code in one place and not needing to deal with ABA issues. The drawback is that it is unclear how to scan abandoned segments efficiently in that case as they -would be spread among all other segments in the regions. +would be spread among all other segments in the arenas. ----------------------------------------------------------- */ // Use the bottom 20-bits (on 64-bit) of the aligned segment pointers @@ -896,19 +1108,19 @@ static mi_decl_cache_align _Atomic(mi_segment_t*) abandoned_visited; // = static mi_decl_cache_align _Atomic(mi_tagged_segment_t) abandoned; // = NULL // Maintain these for debug purposes (these counts may be a bit off) -static mi_decl_cache_align _Atomic(uintptr_t) abandoned_count; -static mi_decl_cache_align _Atomic(uintptr_t) abandoned_visited_count; +static mi_decl_cache_align _Atomic(size_t) abandoned_count; +static mi_decl_cache_align _Atomic(size_t) abandoned_visited_count; // We also maintain a count of current readers of the abandoned list // in order to prevent resetting/decommitting segment memory if it might // still be read. -static mi_decl_cache_align _Atomic(uintptr_t) abandoned_readers; // = 0 +static mi_decl_cache_align _Atomic(size_t) abandoned_readers; // = 0 // Push on the visited list static void mi_abandoned_visited_push(mi_segment_t* segment) { mi_assert_internal(segment->thread_id == 0); mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_segment_t,&segment->abandoned_next) == NULL); - mi_assert_internal(segment->next == NULL && segment->prev == NULL); + mi_assert_internal(segment->next == NULL); mi_assert_internal(segment->used > 0); mi_segment_t* anext = mi_atomic_load_ptr_relaxed(mi_segment_t, &abandoned_visited); do { @@ -931,7 +1143,7 @@ static bool mi_abandoned_visited_revisit(void) mi_tagged_segment_t afirst; mi_tagged_segment_t ts = mi_atomic_load_relaxed(&abandoned); if (mi_tagged_segment_ptr(ts)==NULL) { - uintptr_t count = mi_atomic_load_relaxed(&abandoned_visited_count); + size_t count = mi_atomic_load_relaxed(&abandoned_visited_count); afirst = mi_tagged_segment(first, ts); if (mi_atomic_cas_strong_acq_rel(&abandoned, &ts, afirst)) { mi_atomic_add_relaxed(&abandoned_count, count); @@ -950,7 +1162,7 @@ static bool mi_abandoned_visited_revisit(void) // and atomically prepend to the abandoned list // (no need to increase the readers as we don't access the abandoned segments) mi_tagged_segment_t anext = mi_atomic_load_relaxed(&abandoned); - uintptr_t count; + size_t count; do { count = mi_atomic_load_relaxed(&abandoned_visited_count); mi_atomic_store_ptr_release(mi_segment_t, &last->abandoned_next, mi_tagged_segment_ptr(anext)); @@ -965,7 +1177,7 @@ static bool mi_abandoned_visited_revisit(void) static void mi_abandoned_push(mi_segment_t* segment) { mi_assert_internal(segment->thread_id == 0); mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_segment_t, &segment->abandoned_next) == NULL); - mi_assert_internal(segment->next == NULL && segment->prev == NULL); + mi_assert_internal(segment->next == NULL); mi_assert_internal(segment->used > 0); mi_tagged_segment_t next; mi_tagged_segment_t ts = mi_atomic_load_relaxed(&abandoned); @@ -977,8 +1189,9 @@ static void mi_abandoned_push(mi_segment_t* segment) { } // Wait until there are no more pending reads on segments that used to be in the abandoned list +// called for example from `arena.c` before decommitting void _mi_abandoned_await_readers(void) { - uintptr_t n; + size_t n; do { n = mi_atomic_load_acquire(&abandoned_readers); if (n != 0) mi_atomic_yield(); @@ -991,8 +1204,8 @@ static mi_segment_t* mi_abandoned_pop(void) { // Check efficiently if it is empty (or if the visited list needs to be moved) mi_tagged_segment_t ts = mi_atomic_load_relaxed(&abandoned); segment = mi_tagged_segment_ptr(ts); - if (mi_likely(segment == NULL)) { - if (mi_likely(!mi_abandoned_visited_revisit())) { // try to swap in the visited list on NULL + if mi_likely(segment == NULL) { + if mi_likely(!mi_abandoned_visited_revisit()) { // try to swap in the visited list on NULL return NULL; } } @@ -1027,20 +1240,31 @@ static void mi_segment_abandon(mi_segment_t* segment, mi_segments_tld_t* tld) { mi_assert_internal(segment->used == segment->abandoned); mi_assert_internal(segment->used > 0); mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_segment_t, &segment->abandoned_next) == NULL); - mi_assert_expensive(mi_segment_is_valid(segment, tld)); - - // remove the segment from the free page queue if needed - mi_reset_delayed(tld); - mi_pages_reset_remove_all_in_segment(segment, mi_option_is_enabled(mi_option_abandoned_page_reset), tld); - mi_segment_remove_from_free_queue(segment, tld); - mi_assert_internal(segment->next == NULL && segment->prev == NULL); + mi_assert_internal(segment->abandoned_visits == 0); + mi_assert_expensive(mi_segment_is_valid(segment,tld)); + + // remove the free pages from the free page queues + mi_slice_t* slice = &segment->slices[0]; + const mi_slice_t* end = mi_segment_slices_end(segment); + while (slice < end) { + mi_assert_internal(slice->slice_count > 0); + mi_assert_internal(slice->slice_offset == 0); + if (slice->xblock_size == 0) { // a free page + mi_segment_span_remove_from_queue(slice,tld); + slice->xblock_size = 0; // but keep it free + } + slice = slice + slice->slice_count; + } + // perform delayed decommits + mi_segment_delayed_decommit(segment, mi_option_is_enabled(mi_option_abandoned_page_decommit) /* force? */, tld->stats); + // all pages in the segment are abandoned; add it to the abandoned list _mi_stat_increase(&tld->stats->segments_abandoned, 1); - mi_segments_track_size(-((long)segment->segment_size), tld); + mi_segments_track_size(-((long)mi_segment_size(segment)), tld); segment->thread_id = 0; - segment->abandoned_visits = 0; mi_atomic_store_ptr_release(mi_segment_t, &segment->abandoned_next, NULL); + segment->abandoned_visits = 1; // from 0 to 1 to signify it is abandoned mi_abandoned_push(segment); } @@ -1049,9 +1273,10 @@ void _mi_segment_page_abandon(mi_page_t* page, mi_segments_tld_t* tld) { mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE); mi_assert_internal(mi_page_heap(page) == NULL); mi_segment_t* segment = _mi_page_segment(page); - mi_assert_expensive(!mi_pages_reset_contains(page, tld)); - mi_assert_expensive(mi_segment_is_valid(segment, tld)); - segment->abandoned++; + + mi_assert_expensive(mi_segment_is_valid(segment,tld)); + segment->abandoned++; + _mi_stat_increase(&tld->stats->pages_abandoned, 1); mi_assert_internal(segment->abandoned <= segment->used); if (segment->used == segment->abandoned) { @@ -1064,75 +1289,96 @@ void _mi_segment_page_abandon(mi_page_t* page, mi_segments_tld_t* tld) { Reclaim abandoned pages ----------------------------------------------------------- */ -// Possibly clear pages and check if free space is available -static bool mi_segment_check_free(mi_segment_t* segment, size_t block_size, bool* all_pages_free) +static mi_slice_t* mi_slices_start_iterate(mi_segment_t* segment, const mi_slice_t** end) { + mi_slice_t* slice = &segment->slices[0]; + *end = mi_segment_slices_end(segment); + mi_assert_internal(slice->slice_count>0 && slice->xblock_size>0); // segment allocated page + slice = slice + slice->slice_count; // skip the first segment allocated page + return slice; +} + +// Possibly free pages and check if free space is available +static bool mi_segment_check_free(mi_segment_t* segment, size_t slices_needed, size_t block_size, mi_segments_tld_t* tld) { mi_assert_internal(block_size < MI_HUGE_BLOCK_SIZE); + mi_assert_internal(mi_segment_is_abandoned(segment)); bool has_page = false; - size_t pages_used = 0; - size_t pages_used_empty = 0; - for (size_t i = 0; i < segment->capacity; i++) { - mi_page_t* page = &segment->pages[i]; - if (page->segment_in_use) { - pages_used++; + + // for all slices + const mi_slice_t* end; + mi_slice_t* slice = mi_slices_start_iterate(segment, &end); + while (slice < end) { + mi_assert_internal(slice->slice_count > 0); + mi_assert_internal(slice->slice_offset == 0); + if (mi_slice_is_used(slice)) { // used page // ensure used count is up to date and collect potential concurrent frees + mi_page_t* const page = mi_slice_to_page(slice); _mi_page_free_collect(page, false); if (mi_page_all_free(page)) { - // if everything free already, page can be reused for some block size - // note: don't clear the page yet as we can only OS reset it once it is reclaimed - pages_used_empty++; - has_page = true; - } - else if (page->xblock_size == block_size && mi_page_has_any_available(page)) { - // a page has available free blocks of the right size - has_page = true; + // if this page is all free now, free it without adding to any queues (yet) + mi_assert_internal(page->next == NULL && page->prev==NULL); + _mi_stat_decrease(&tld->stats->pages_abandoned, 1); + segment->abandoned--; + slice = mi_segment_page_clear(page, tld); // re-assign slice due to coalesce! + mi_assert_internal(!mi_slice_is_used(slice)); + if (slice->slice_count >= slices_needed) { + has_page = true; + } } + else { + if (page->xblock_size == block_size && mi_page_has_any_available(page)) { + // a page has available free blocks of the right size + has_page = true; + } + } } else { - // whole empty page - has_page = true; + // empty span + if (slice->slice_count >= slices_needed) { + has_page = true; + } } - } - mi_assert_internal(pages_used == segment->used && pages_used >= pages_used_empty); - if (all_pages_free != NULL) { - *all_pages_free = ((pages_used - pages_used_empty) == 0); + slice = slice + slice->slice_count; } return has_page; } - -// Reclaim a segment; returns NULL if the segment was freed +// Reclaim an abandoned segment; returns NULL if the segment was freed // set `right_page_reclaimed` to `true` if it reclaimed a page of the right `block_size` that was not full. static mi_segment_t* mi_segment_reclaim(mi_segment_t* segment, mi_heap_t* heap, size_t requested_block_size, bool* right_page_reclaimed, mi_segments_tld_t* tld) { mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_segment_t, &segment->abandoned_next) == NULL); + mi_assert_expensive(mi_segment_is_valid(segment, tld)); if (right_page_reclaimed != NULL) { *right_page_reclaimed = false; } segment->thread_id = _mi_thread_id(); segment->abandoned_visits = 0; - mi_segments_track_size((long)segment->segment_size, tld); - mi_assert_internal(segment->next == NULL && segment->prev == NULL); - mi_assert_expensive(mi_segment_is_valid(segment, tld)); + mi_segments_track_size((long)mi_segment_size(segment), tld); + mi_assert_internal(segment->next == NULL); _mi_stat_decrease(&tld->stats->segments_abandoned, 1); - - for (size_t i = 0; i < segment->capacity; i++) { - mi_page_t* page = &segment->pages[i]; - if (page->segment_in_use) { + + // for all slices + const mi_slice_t* end; + mi_slice_t* slice = mi_slices_start_iterate(segment, &end); + while (slice < end) { + mi_assert_internal(slice->slice_count > 0); + mi_assert_internal(slice->slice_offset == 0); + if (mi_slice_is_used(slice)) { + // in use: reclaim the page in our heap + mi_page_t* page = mi_slice_to_page(slice); mi_assert_internal(!page->is_reset); mi_assert_internal(page->is_committed); - mi_assert_internal(mi_page_not_in_queue(page, tld)); mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE); mi_assert_internal(mi_page_heap(page) == NULL); - segment->abandoned--; - mi_assert(page->next == NULL); + mi_assert_internal(page->next == NULL && page->prev==NULL); _mi_stat_decrease(&tld->stats->pages_abandoned, 1); - // set the heap again and allow heap thread delayed free again. + segment->abandoned--; + // set the heap again and allow delayed free again mi_page_set_heap(page, heap); _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, true); // override never (after heap is set) - // TODO: should we not collect again given that we just collected in `check_free`? _mi_page_free_collect(page, false); // ensure used count is up to date if (mi_page_all_free(page)) { - // if everything free already, clear the page directly - mi_segment_page_clear(segment, page, true, tld); // reset is ok now + // if everything free by now, free the page + slice = mi_segment_page_clear(page, tld); // set slice again due to coalesceing } else { // otherwise reclaim it into the heap @@ -1142,21 +1388,21 @@ static mi_segment_t* mi_segment_reclaim(mi_segment_t* segment, mi_heap_t* heap, } } } - else if (page->is_committed && !page->is_reset) { // not in-use, and not reset yet - // note: do not reset as this includes pages that were not touched before - // mi_pages_reset_add(segment, page, tld); + else { + // the span is free, add it to our page queues + slice = mi_segment_span_free_coalesce(slice, tld); // set slice again due to coalesceing } + mi_assert_internal(slice->slice_count>0 && slice->slice_offset==0); + slice = slice + slice->slice_count; } - mi_assert_internal(segment->abandoned == 0); - if (segment->used == 0) { + + mi_assert(segment->abandoned == 0); + if (segment->used == 0) { // due to page_clear mi_assert_internal(right_page_reclaimed == NULL || !(*right_page_reclaimed)); mi_segment_free(segment, false, tld); return NULL; } else { - if (segment->page_kind <= MI_PAGE_MEDIUM && mi_segment_has_free(segment)) { - mi_segment_insert_in_free_queue(segment, tld); - } return segment; } } @@ -1169,16 +1415,18 @@ void _mi_abandoned_reclaim_all(mi_heap_t* heap, mi_segments_tld_t* tld) { } } -static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t block_size, mi_page_kind_t page_kind, bool* reclaimed, mi_segments_tld_t* tld) +static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t needed_slices, size_t block_size, bool* reclaimed, mi_segments_tld_t* tld) { *reclaimed = false; mi_segment_t* segment; - int max_tries = 8; // limit the work to bound allocation times + long max_tries = mi_option_get_clamp(mi_option_max_segment_reclaim, 8, 1024); // limit the work to bound allocation times while ((max_tries-- > 0) && ((segment = mi_abandoned_pop()) != NULL)) { segment->abandoned_visits++; - bool all_pages_free; - bool has_page = mi_segment_check_free(segment,block_size,&all_pages_free); // try to free up pages (due to concurrent frees) - if (all_pages_free) { + // todo: an arena exclusive heap will potentially visit many abandoned unsuitable segments + // and push them into the visited list and use many tries. Perhaps we can skip non-suitable ones in a better way? + bool is_suitable = _mi_heap_memid_is_suitable(heap, segment->memid); + bool has_page = mi_segment_check_free(segment,needed_slices,block_size,tld); // try to free up pages (due to concurrent frees) + if (segment->used == 0) { // free the segment (by forced reclaim) to make it available to other threads. // note1: we prefer to free a segment as that might lead to reclaiming another // segment that is still partially used. @@ -1186,18 +1434,19 @@ static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t block_size, // freeing but that would violate some invariants temporarily) mi_segment_reclaim(segment, heap, 0, NULL, tld); } - else if (has_page && segment->page_kind == page_kind) { - // found a free page of the right kind, or page of the right block_size with free space + else if (has_page && is_suitable) { + // found a large enough free span, or a page of the right block_size with free space // we return the result of reclaim (which is usually `segment`) as it might free // the segment due to concurrent frees (in which case `NULL` is returned). return mi_segment_reclaim(segment, heap, block_size, reclaimed, tld); } - else if (segment->abandoned_visits >= 3) { - // always reclaim on 3rd visit to limit the list length. + else if (segment->abandoned_visits > 3 && is_suitable) { + // always reclaim on 3rd visit to limit the abandoned queue length. mi_segment_reclaim(segment, heap, 0, NULL, tld); } else { // otherwise, push on the visited list so it gets not looked at too quickly again + mi_segment_delayed_decommit(segment, true /* force? */, tld->stats); // forced decommit if needed as we may not visit soon again mi_abandoned_visited_push(segment); } } @@ -1205,128 +1454,134 @@ static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t block_size, } +void _mi_abandoned_collect(mi_heap_t* heap, bool force, mi_segments_tld_t* tld) +{ + mi_segment_t* segment; + int max_tries = (force ? 16*1024 : 1024); // limit latency + if (force) { + mi_abandoned_visited_revisit(); + } + while ((max_tries-- > 0) && ((segment = mi_abandoned_pop()) != NULL)) { + mi_segment_check_free(segment,0,0,tld); // try to free up pages (due to concurrent frees) + if (segment->used == 0) { + // free the segment (by forced reclaim) to make it available to other threads. + // note: we could in principle optimize this by skipping reclaim and directly + // freeing but that would violate some invariants temporarily) + mi_segment_reclaim(segment, heap, 0, NULL, tld); + } + else { + // otherwise, decommit if needed and push on the visited list + // note: forced decommit can be expensive if many threads are destroyed/created as in mstress. + mi_segment_delayed_decommit(segment, force, tld->stats); + mi_abandoned_visited_push(segment); + } + } +} + /* ----------------------------------------------------------- Reclaim or allocate ----------------------------------------------------------- */ -static mi_segment_t* mi_segment_reclaim_or_alloc(mi_heap_t* heap, size_t block_size, mi_page_kind_t page_kind, size_t page_shift, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) +static mi_segment_t* mi_segment_reclaim_or_alloc(mi_heap_t* heap, size_t needed_slices, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { - mi_assert_internal(page_kind <= MI_PAGE_LARGE); mi_assert_internal(block_size < MI_HUGE_BLOCK_SIZE); - // 1. try to get a segment from our cache - mi_segment_t* segment = mi_segment_cache_pop(MI_SEGMENT_SIZE, tld); - if (segment != NULL) { - mi_segment_init(segment, 0, page_kind, page_shift, tld, os_tld); - return segment; - } - // 2. try to reclaim an abandoned segment + mi_assert_internal(block_size <= MI_LARGE_OBJ_SIZE_MAX); + + // 1. try to reclaim an abandoned segment bool reclaimed; - segment = mi_segment_try_reclaim(heap, block_size, page_kind, &reclaimed, tld); + mi_segment_t* segment = mi_segment_try_reclaim(heap, needed_slices, block_size, &reclaimed, tld); if (reclaimed) { // reclaimed the right page right into the heap - mi_assert_internal(segment != NULL && segment->page_kind == page_kind && page_kind <= MI_PAGE_LARGE); + mi_assert_internal(segment != NULL); return NULL; // pretend out-of-memory as the page will be in the page queue of the heap with available blocks } else if (segment != NULL) { - // reclaimed a segment with empty pages (of `page_kind`) in it + // reclaimed a segment with a large enough empty span in it return segment; } - // 3. otherwise allocate a fresh segment - return mi_segment_alloc(0, page_kind, page_shift, tld, os_tld); + // 2. otherwise allocate a fresh segment + return mi_segment_alloc(0, 0, heap->arena_id, tld, os_tld, NULL); } /* ----------------------------------------------------------- - Small page allocation + Page allocation ----------------------------------------------------------- */ -static mi_page_t* mi_segment_find_free(mi_segment_t* segment, mi_segments_tld_t* tld) { - mi_assert_internal(mi_segment_has_free(segment)); - mi_assert_expensive(mi_segment_is_valid(segment, tld)); - for (size_t i = 0; i < segment->capacity; i++) { // TODO: use a bitmap instead of search? - mi_page_t* page = &segment->pages[i]; - if (!page->segment_in_use) { - bool ok = mi_segment_page_claim(segment, page, tld); - if (ok) return page; +static mi_page_t* mi_segments_page_alloc(mi_heap_t* heap, mi_page_kind_t page_kind, size_t required, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) +{ + mi_assert_internal(required <= MI_LARGE_OBJ_SIZE_MAX && page_kind <= MI_PAGE_LARGE); + + // find a free page + size_t page_size = _mi_align_up(required, (required > MI_MEDIUM_PAGE_SIZE ? MI_MEDIUM_PAGE_SIZE : MI_SEGMENT_SLICE_SIZE)); + size_t slices_needed = page_size / MI_SEGMENT_SLICE_SIZE; + mi_assert_internal(slices_needed * MI_SEGMENT_SLICE_SIZE == page_size); + mi_page_t* page = mi_segments_page_find_and_allocate(slices_needed, heap->arena_id, tld); //(required <= MI_SMALL_SIZE_MAX ? 0 : slices_needed), tld); + if (page==NULL) { + // no free page, allocate a new segment and try again + if (mi_segment_reclaim_or_alloc(heap, slices_needed, block_size, tld, os_tld) == NULL) { + // OOM or reclaimed a good page in the heap + return NULL; + } + else { + // otherwise try again + return mi_segments_page_alloc(heap, page_kind, required, block_size, tld, os_tld); } } - mi_assert(false); - return NULL; -} - -// Allocate a page inside a segment. Requires that the page has free pages -static mi_page_t* mi_segment_page_alloc_in(mi_segment_t* segment, mi_segments_tld_t* tld) { - mi_assert_internal(mi_segment_has_free(segment)); - return mi_segment_find_free(segment, tld); -} - -static mi_page_t* mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, mi_page_kind_t kind, size_t page_shift, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { - // find an available segment the segment free queue - mi_segment_queue_t* const free_queue = mi_segment_free_queue_of_kind(kind, tld); - if (mi_segment_queue_is_empty(free_queue)) { - // possibly allocate or reclaim a fresh segment - mi_segment_t* const segment = mi_segment_reclaim_or_alloc(heap, block_size, kind, page_shift, tld, os_tld); - if (segment == NULL) return NULL; // return NULL if out-of-memory (or reclaimed) - mi_assert_internal(free_queue->first == segment); - mi_assert_internal(segment->page_kind==kind); - mi_assert_internal(segment->used < segment->capacity); - } - mi_assert_internal(free_queue->first != NULL); - mi_page_t* const page = mi_segment_page_alloc_in(free_queue->first, tld); - mi_assert_internal(page != NULL); -#if MI_DEBUG>=2 - // verify it is committed - _mi_segment_page_start(_mi_page_segment(page), page, sizeof(void*), NULL, NULL)[0] = 0; -#endif + mi_assert_internal(page != NULL && page->slice_count*MI_SEGMENT_SLICE_SIZE == page_size); + mi_assert_internal(_mi_ptr_segment(page)->thread_id == _mi_thread_id()); + mi_segment_delayed_decommit(_mi_ptr_segment(page), false, tld->stats); return page; } -static mi_page_t* mi_segment_small_page_alloc(mi_heap_t* heap, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { - return mi_segment_page_alloc(heap, block_size, MI_PAGE_SMALL,MI_SMALL_PAGE_SHIFT,tld,os_tld); -} -static mi_page_t* mi_segment_medium_page_alloc(mi_heap_t* heap, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { - return mi_segment_page_alloc(heap, block_size, MI_PAGE_MEDIUM, MI_MEDIUM_PAGE_SHIFT, tld, os_tld); -} /* ----------------------------------------------------------- - large page allocation + Huge page allocation ----------------------------------------------------------- */ -static mi_page_t* mi_segment_large_page_alloc(mi_heap_t* heap, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { - mi_segment_t* segment = mi_segment_reclaim_or_alloc(heap,block_size,MI_PAGE_LARGE,MI_LARGE_PAGE_SHIFT,tld,os_tld); - if (segment == NULL) return NULL; - mi_page_t* page = mi_segment_find_free(segment, tld); - mi_assert_internal(page != NULL); -#if MI_DEBUG>=2 - _mi_segment_page_start(segment, page, sizeof(void*), NULL, NULL)[0] = 0; -#endif - return page; -} - -static mi_page_t* mi_segment_huge_page_alloc(size_t size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) +static mi_page_t* mi_segment_huge_page_alloc(size_t size, size_t page_alignment, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { - mi_segment_t* segment = mi_segment_alloc(size, MI_PAGE_HUGE, MI_SEGMENT_SHIFT,tld,os_tld); - if (segment == NULL) return NULL; - mi_assert_internal(mi_segment_page_size(segment) - segment->segment_info_size - (2*(MI_SECURE == 0 ? 0 : _mi_os_page_size())) >= size); - segment->thread_id = 0; // huge pages are immediately abandoned - mi_segments_track_size(-(long)segment->segment_size, tld); - mi_page_t* page = mi_segment_find_free(segment, tld); - mi_assert_internal(page != NULL); + mi_page_t* page = NULL; + mi_segment_t* segment = mi_segment_alloc(size,page_alignment,req_arena_id,tld,os_tld,&page); + if (segment == NULL || page==NULL) return NULL; + mi_assert_internal(segment->used==1); + mi_assert_internal(mi_page_block_size(page) >= size); + #if MI_HUGE_PAGE_ABANDON + segment->thread_id = 0; // huge segments are immediately abandoned + #endif + + // for huge pages we initialize the xblock_size as we may + // overallocate to accommodate large alignments. + size_t psize; + uint8_t* start = _mi_segment_page_start(segment, page, &psize); + page->xblock_size = (psize > MI_HUGE_BLOCK_SIZE ? MI_HUGE_BLOCK_SIZE : (uint32_t)psize); + + // decommit the part of the prefix of a page that will not be used; this can be quite large (close to MI_SEGMENT_SIZE) + if (page_alignment > 0 && segment->allow_decommit) { + uint8_t* aligned_p = (uint8_t*)_mi_align_up((uintptr_t)start, page_alignment); + mi_assert_internal(_mi_is_aligned(aligned_p, page_alignment)); + mi_assert_internal(psize - (aligned_p - start) >= size); + uint8_t* decommit_start = start + sizeof(mi_block_t); // for the free list + ptrdiff_t decommit_size = aligned_p - decommit_start; + _mi_os_decommit(decommit_start, decommit_size, &_mi_stats_main); // note: cannot use segment_decommit on huge segments + } + return page; } +#if MI_HUGE_PAGE_ABANDON // free huge block from another thread void _mi_segment_huge_page_free(mi_segment_t* segment, mi_page_t* page, mi_block_t* block) { // huge page segments are always abandoned and can be freed immediately by any thread - mi_assert_internal(segment->page_kind==MI_PAGE_HUGE); + mi_assert_internal(segment->kind==MI_SEGMENT_HUGE); mi_assert_internal(segment == _mi_page_segment(page)); mi_assert_internal(mi_atomic_load_relaxed(&segment->thread_id)==0); // claim it and free mi_heap_t* heap = mi_heap_get_default(); // issue #221; don't use the internal get_default_heap as we need to ensure the thread is initialized. // paranoia: if this it the last reference, the cas should always succeed - uintptr_t expected_tid = 0; + size_t expected_tid = 0; if (mi_atomic_cas_strong_acq_rel(&segment->thread_id, &expected_tid, heap->thread_id)) { mi_block_set_next(page, block, page->free); page->free = block; @@ -1334,7 +1589,6 @@ void _mi_segment_huge_page_free(mi_segment_t* segment, mi_page_t* page, mi_block page->is_zero = false; mi_assert(page->used == 0); mi_tld_t* tld = heap->tld; - mi_segments_track_size((long)segment->segment_size, &tld->segments); _mi_segment_page_free(page, true, &tld->segments); } #if (MI_DEBUG!=0) @@ -1344,27 +1598,48 @@ void _mi_segment_huge_page_free(mi_segment_t* segment, mi_page_t* page, mi_block #endif } +#else +// reset memory of a huge block from another thread +void _mi_segment_huge_page_reset(mi_segment_t* segment, mi_page_t* page, mi_block_t* block) { + MI_UNUSED(page); + mi_assert_internal(segment->kind == MI_SEGMENT_HUGE); + mi_assert_internal(segment == _mi_page_segment(page)); + mi_assert_internal(page->used == 1); // this is called just before the free + mi_assert_internal(page->free == NULL); + if (segment->allow_decommit) { + const size_t csize = mi_usable_size(block) - sizeof(mi_block_t); + uint8_t* p = (uint8_t*)block + sizeof(mi_block_t); + _mi_os_decommit(p, csize, &_mi_stats_main); // note: cannot use segment_decommit on huge segments + } +} +#endif + /* ----------------------------------------------------------- - Page allocation + Page allocation and free ----------------------------------------------------------- */ - -mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { +mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, size_t page_alignment, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { mi_page_t* page; - if (block_size <= MI_SMALL_OBJ_SIZE_MAX) { - page = mi_segment_small_page_alloc(heap, block_size, tld, os_tld); + if mi_unlikely(page_alignment > MI_ALIGNMENT_MAX) { + mi_assert_internal(_mi_is_power_of_two(page_alignment)); + mi_assert_internal(page_alignment >= MI_SEGMENT_SIZE); + if (page_alignment < MI_SEGMENT_SIZE) { page_alignment = MI_SEGMENT_SIZE; } + page = mi_segment_huge_page_alloc(block_size,page_alignment,heap->arena_id,tld,os_tld); + } + else if (block_size <= MI_SMALL_OBJ_SIZE_MAX) { + page = mi_segments_page_alloc(heap,MI_PAGE_SMALL,block_size,block_size,tld,os_tld); } else if (block_size <= MI_MEDIUM_OBJ_SIZE_MAX) { - page = mi_segment_medium_page_alloc(heap, block_size, tld, os_tld); + page = mi_segments_page_alloc(heap,MI_PAGE_MEDIUM,MI_MEDIUM_PAGE_SIZE,block_size,tld, os_tld); } else if (block_size <= MI_LARGE_OBJ_SIZE_MAX) { - page = mi_segment_large_page_alloc(heap, block_size, tld, os_tld); + page = mi_segments_page_alloc(heap,MI_PAGE_LARGE,block_size,block_size,tld, os_tld); } else { - page = mi_segment_huge_page_alloc(block_size,tld,os_tld); + page = mi_segment_huge_page_alloc(block_size,page_alignment,heap->arena_id,tld,os_tld); } + mi_assert_internal(page == NULL || _mi_heap_memid_is_suitable(heap, _mi_page_segment(page)->memid)); mi_assert_expensive(page == NULL || mi_segment_is_valid(_mi_page_segment(page),tld)); - mi_assert_internal(page == NULL || (mi_segment_page_size(_mi_page_segment(page)) - (MI_SECURE == 0 ? 0 : _mi_os_page_size())) >= block_size); - mi_reset_delayed(tld); - mi_assert_internal(page == NULL || mi_page_not_in_queue(page, tld)); return page; } + + diff --git a/Source/mimalloc/src/static.c b/Source/mimalloc/src/static.c index 4b3abc285..d992f4daf 100644 --- a/Source/mimalloc/src/static.c +++ b/Source/mimalloc/src/static.c @@ -14,26 +14,27 @@ terms of the MIT license. A copy of the license can be found in the file #endif #include "mimalloc.h" -#include "mimalloc-internal.h" +#include "mimalloc/internal.h" // For a static override we create a single object file // containing the whole library. If it is linked first // it will override all the standard library allocation // functions (on Unix's). -#include "stats.c" -#include "random.c" -#include "os.c" -#include "bitmap.c" -#include "arena.c" -#include "region.c" -#include "segment.c" -#include "page.c" -#include "heap.c" -#include "alloc.c" +#include "alloc.c" // includes alloc-override.c #include "alloc-aligned.c" #include "alloc-posix.c" -#if MI_OSX_ZONE -#include "alloc-override-osx.c" -#endif +#include "arena.c" +#include "bitmap.c" +#include "heap.c" #include "init.c" #include "options.c" +#include "os.c" +#include "page.c" // includes page-queue.c +#include "random.c" +#include "segment.c" +#include "segment-cache.c" +#include "stats.c" +#include "prim/prim.c" +#if MI_OSX_ZONE +#include "prim/osx/alloc-override-zone.c" +#endif diff --git a/Source/mimalloc/src/stats.c b/Source/mimalloc/src/stats.c index c94fbde9d..d2a316818 100644 --- a/Source/mimalloc/src/stats.c +++ b/Source/mimalloc/src/stats.c @@ -5,10 +5,11 @@ terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ #include "mimalloc.h" -#include "mimalloc-internal.h" -#include "mimalloc-atomic.h" +#include "mimalloc/internal.h" +#include "mimalloc/atomic.h" +#include "mimalloc/prim.h" -#include // fputs, stderr +#include // snprintf #include // memset #if defined(_MSC_VER) && (_MSC_VER < 1920) @@ -21,7 +22,7 @@ terms of the MIT license. A copy of the license can be found in the file static bool mi_is_in_main(void* stat) { return ((uint8_t*)stat >= (uint8_t*)&_mi_stats_main - && (uint8_t*)stat < ((uint8_t*)&_mi_stats_main + sizeof(mi_stats_t))); + && (uint8_t*)stat < ((uint8_t*)&_mi_stats_main + sizeof(mi_stats_t))); } static void mi_stat_update(mi_stat_count_t* stat, int64_t amount) { @@ -51,7 +52,7 @@ static void mi_stat_update(mi_stat_count_t* stat, int64_t amount) { } } -void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount) { +void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount) { if (mi_is_in_main(stat)) { mi_atomic_addi64_relaxed( &stat->count, 1 ); mi_atomic_addi64_relaxed( &stat->total, (int64_t)amount ); @@ -77,7 +78,7 @@ static void mi_stat_add(mi_stat_count_t* stat, const mi_stat_count_t* src, int64 mi_atomic_addi64_relaxed( &stat->allocated, src->allocated * unit); mi_atomic_addi64_relaxed( &stat->current, src->current * unit); mi_atomic_addi64_relaxed( &stat->freed, src->freed * unit); - // peak scores do not work across threads.. + // peak scores do not work across threads.. mi_atomic_addi64_relaxed( &stat->peak, src->peak * unit); } @@ -105,7 +106,7 @@ static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) { mi_stat_add(&stats->segments_cache, &src->segments_cache, 1); mi_stat_add(&stats->normal, &src->normal, 1); mi_stat_add(&stats->huge, &src->huge, 1); - mi_stat_add(&stats->giant, &src->giant, 1); + mi_stat_add(&stats->large, &src->large, 1); mi_stat_counter_add(&stats->pages_extended, &src->pages_extended, 1); mi_stat_counter_add(&stats->mmap_calls, &src->mmap_calls, 1); @@ -115,7 +116,7 @@ static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) { mi_stat_counter_add(&stats->searches, &src->searches, 1); mi_stat_counter_add(&stats->normal_count, &src->normal_count, 1); mi_stat_counter_add(&stats->huge_count, &src->huge_count, 1); - mi_stat_counter_add(&stats->giant_count, &src->giant_count, 1); + mi_stat_counter_add(&stats->large_count, &src->large_count, 1); #if MI_STAT>1 for (size_t i = 0; i <= MI_BIN_HUGE; i++) { if (src->normal_bins[i].allocated > 0 || src->normal_bins[i].freed > 0) { @@ -129,29 +130,33 @@ static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) { Display statistics ----------------------------------------------------------- */ -// unit > 0 : size in binary bytes +// unit > 0 : size in binary bytes // unit == 0: count as decimal // unit < 0 : count in binary static void mi_printf_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg, const char* fmt) { - char buf[32]; + char buf[32]; buf[0] = 0; int len = 32; - const char* suffix = (unit <= 0 ? " " : "b"); + const char* suffix = (unit <= 0 ? " " : "B"); const int64_t base = (unit == 0 ? 1000 : 1024); if (unit>0) n *= unit; const int64_t pos = (n < 0 ? -n : n); if (pos < base) { - snprintf(buf, len, "%d %s ", (int)n, suffix); + if (n!=1 || suffix[0] != 'B') { // skip printing 1 B for the unit column + snprintf(buf, len, "%d %-3s", (int)n, (n==0 ? "" : suffix)); + } } else { int64_t divider = base; - const char* magnitude = "k"; - if (pos >= divider*base) { divider *= base; magnitude = "m"; } - if (pos >= divider*base) { divider *= base; magnitude = "g"; } + const char* magnitude = "K"; + if (pos >= divider*base) { divider *= base; magnitude = "M"; } + if (pos >= divider*base) { divider *= base; magnitude = "G"; } const int64_t tens = (n / (divider/10)); const long whole = (long)(tens/10); const long frac1 = (long)(tens%10); - snprintf(buf, len, "%ld.%ld %s%s", whole, (frac1 < 0 ? -frac1 : frac1), magnitude, suffix); + char unitdesc[8]; + snprintf(unitdesc, 8, "%s%s%s", magnitude, (base==1024 ? "i" : ""), suffix); + snprintf(buf, len, "%ld.%ld %-3s", whole, (frac1 < 0 ? -frac1 : frac1), unitdesc); } _mi_fprintf(out, arg, (fmt==NULL ? "%11s" : fmt), buf); } @@ -166,19 +171,23 @@ static void mi_print_count(int64_t n, int64_t unit, mi_output_fun* out, void* ar else mi_print_amount(n,0,out,arg); } -static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg ) { +static void mi_stat_print_ex(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg, const char* notok ) { _mi_fprintf(out, arg,"%10s:", msg); - if (unit>0) { + if (unit > 0) { mi_print_amount(stat->peak, unit, out, arg); mi_print_amount(stat->allocated, unit, out, arg); mi_print_amount(stat->freed, unit, out, arg); mi_print_amount(stat->current, unit, out, arg); mi_print_amount(unit, 1, out, arg); mi_print_count(stat->allocated, unit, out, arg); - if (stat->allocated > stat->freed) - _mi_fprintf(out, arg, " not all freed!\n"); - else + if (stat->allocated > stat->freed) { + _mi_fprintf(out, arg, " "); + _mi_fprintf(out, arg, (notok == NULL ? "not all freed!" : notok)); + _mi_fprintf(out, arg, "\n"); + } + else { _mi_fprintf(out, arg, " ok\n"); + } } else if (unit<0) { mi_print_amount(stat->peak, -1, out, arg); @@ -200,12 +209,16 @@ static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t else { mi_print_amount(stat->peak, 1, out, arg); mi_print_amount(stat->allocated, 1, out, arg); - _mi_fprintf(out, arg, "%11s", " "); // no freed + _mi_fprintf(out, arg, "%11s", " "); // no freed mi_print_amount(stat->current, 1, out, arg); _mi_fprintf(out, arg, "\n"); } } +static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg) { + mi_stat_print_ex(stat, msg, unit, out, arg, NULL); +} + static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg ) { _mi_fprintf(out, arg, "%10s:", msg); mi_print_amount(stat->total, -1, out, arg); @@ -213,7 +226,7 @@ static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg } static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg) { - const int64_t avg_tens = (stat->count == 0 ? 0 : (stat->total*10 / stat->count)); + const int64_t avg_tens = (stat->count == 0 ? 0 : (stat->total*10 / stat->count)); const long avg_whole = (long)(avg_tens/10); const long avg_frac1 = (long)(avg_tens%10); _mi_fprintf(out, arg, "%10s: %5ld.%ld avg\n", msg, avg_whole, avg_frac1); @@ -221,7 +234,7 @@ static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* static void mi_print_header(mi_output_fun* out, void* arg ) { - _mi_fprintf(out, arg, "%10s: %10s %10s %10s %10s %10s %10s\n", "heap stats", "peak ", "total ", "freed ", "current ", "unit ", "count "); + _mi_fprintf(out, arg, "%10s: %10s %10s %10s %10s %10s %10s\n", "heap stats", "peak ", "total ", "freed ", "current ", "unit ", "count "); } #if MI_STAT>1 @@ -253,7 +266,7 @@ typedef struct buffered_s { mi_output_fun* out; // original output function void* arg; // and state char* buf; // local buffer of at least size `count+1` - size_t used; // currently used chars `used <= count` + size_t used; // currently used chars `used <= count` size_t count; // total chars available for output } buffered_t; @@ -263,7 +276,7 @@ static void mi_buffered_flush(buffered_t* buf) { buf->used = 0; } -static void mi_buffered_out(const char* msg, void* arg) { +static void mi_cdecl mi_buffered_out(const char* msg, void* arg) { buffered_t* buf = (buffered_t*)arg; if (msg==NULL || buf==NULL) return; for (const char* src = msg; *src != 0; src++) { @@ -279,8 +292,6 @@ static void mi_buffered_out(const char* msg, void* arg) { // Print statistics //------------------------------------------------------------ -static void mi_stat_process_info(mi_msecs_t* elapsed, mi_msecs_t* utime, mi_msecs_t* stime, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults); - static void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0) mi_attr_noexcept { // wrap the output function to be line buffered char buf[256]; @@ -296,20 +307,20 @@ static void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0) #endif #if MI_STAT mi_stat_print(&stats->normal, "normal", (stats->normal_count.count == 0 ? 1 : -(stats->normal.allocated / stats->normal_count.count)), out, arg); + mi_stat_print(&stats->large, "large", (stats->large_count.count == 0 ? 1 : -(stats->large.allocated / stats->large_count.count)), out, arg); mi_stat_print(&stats->huge, "huge", (stats->huge_count.count == 0 ? 1 : -(stats->huge.allocated / stats->huge_count.count)), out, arg); - mi_stat_print(&stats->giant, "giant", (stats->giant_count.count == 0 ? 1 : -(stats->giant.allocated / stats->giant_count.count)), out, arg); mi_stat_count_t total = { 0,0,0,0 }; mi_stat_add(&total, &stats->normal, 1); + mi_stat_add(&total, &stats->large, 1); mi_stat_add(&total, &stats->huge, 1); - mi_stat_add(&total, &stats->giant, 1); mi_stat_print(&total, "total", 1, out, arg); #endif #if MI_STAT>1 mi_stat_print(&stats->malloc, "malloc req", 1, out, arg); _mi_fprintf(out, arg, "\n"); #endif - mi_stat_print(&stats->reserved, "reserved", 1, out, arg); - mi_stat_print(&stats->committed, "committed", 1, out, arg); + mi_stat_print_ex(&stats->reserved, "reserved", 1, out, arg, ""); + mi_stat_print_ex(&stats->committed, "committed", 1, out, arg, ""); mi_stat_print(&stats->reset, "reset", 1, out, arg); mi_stat_print(&stats->page_committed, "touched", 1, out, arg); mi_stat_print(&stats->segments, "segments", -1, out, arg); @@ -323,17 +334,17 @@ static void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0) mi_stat_counter_print(&stats->commit_calls, "commits", out, arg); mi_stat_print(&stats->threads, "threads", -1, out, arg); mi_stat_counter_print_avg(&stats->searches, "searches", out, arg); - _mi_fprintf(out, arg, "%10s: %7i\n", "numa nodes", _mi_os_numa_node_count()); - - mi_msecs_t elapsed; - mi_msecs_t user_time; - mi_msecs_t sys_time; + _mi_fprintf(out, arg, "%10s: %7zu\n", "numa nodes", _mi_os_numa_node_count()); + + size_t elapsed; + size_t user_time; + size_t sys_time; size_t current_rss; size_t peak_rss; size_t current_commit; size_t peak_commit; size_t page_faults; - mi_stat_process_info(&elapsed, &user_time, &sys_time, ¤t_rss, &peak_rss, ¤t_commit, &peak_commit, &page_faults); + mi_process_info(&elapsed, &user_time, &sys_time, ¤t_rss, &peak_rss, ¤t_commit, &peak_commit, &page_faults); _mi_fprintf(out, arg, "%10s: %7ld.%03ld s\n", "elapsed", elapsed/1000, elapsed%1000); _mi_fprintf(out, arg, "%10s: user: %ld.%03ld s, system: %ld.%03ld s, faults: %lu, rss: ", "process", user_time/1000, user_time%1000, sys_time/1000, sys_time%1000, (unsigned long)page_faults ); @@ -342,7 +353,7 @@ static void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0) _mi_fprintf(out, arg, ", commit: "); mi_printf_amount((int64_t)peak_commit, 1, out, arg, "%s"); } - _mi_fprintf(out, arg, "\n"); + _mi_fprintf(out, arg, "\n"); } static mi_msecs_t mi_process_start; // = 0 @@ -392,42 +403,12 @@ void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept { // ---------------------------------------------------------------- // Basic timer for convenience; use milli-seconds to avoid doubles // ---------------------------------------------------------------- -#ifdef _WIN32 -#include -static mi_msecs_t mi_to_msecs(LARGE_INTEGER t) { - static LARGE_INTEGER mfreq; // = 0 - if (mfreq.QuadPart == 0LL) { - LARGE_INTEGER f; - QueryPerformanceFrequency(&f); - mfreq.QuadPart = f.QuadPart/1000LL; - if (mfreq.QuadPart == 0) mfreq.QuadPart = 1; - } - return (mi_msecs_t)(t.QuadPart / mfreq.QuadPart); -} + +static mi_msecs_t mi_clock_diff; mi_msecs_t _mi_clock_now(void) { - LARGE_INTEGER t; - QueryPerformanceCounter(&t); - return mi_to_msecs(t); -} -#else -#include -#ifdef CLOCK_REALTIME -mi_msecs_t _mi_clock_now(void) { - struct timespec t; - clock_gettime(CLOCK_REALTIME, &t); - return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); -} -#else -// low resolution timer -mi_msecs_t _mi_clock_now(void) { - return ((mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000)); + return _mi_prim_clock_now(); } -#endif -#endif - - -static mi_msecs_t mi_clock_diff; mi_msecs_t _mi_clock_start(void) { if (mi_clock_diff == 0.0) { @@ -447,129 +428,27 @@ mi_msecs_t _mi_clock_end(mi_msecs_t start) { // Basic process statistics // -------------------------------------------------------- -#if defined(_WIN32) -#include -#include -#pragma comment(lib,"psapi.lib") - -static mi_msecs_t filetime_msecs(const FILETIME* ftime) { - ULARGE_INTEGER i; - i.LowPart = ftime->dwLowDateTime; - i.HighPart = ftime->dwHighDateTime; - mi_msecs_t msecs = (i.QuadPart / 10000); // FILETIME is in 100 nano seconds - return msecs; -} - -static void mi_stat_process_info(mi_msecs_t* elapsed, mi_msecs_t* utime, mi_msecs_t* stime, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults) -{ - *elapsed = _mi_clock_end(mi_process_start); - FILETIME ct; - FILETIME ut; - FILETIME st; - FILETIME et; - GetProcessTimes(GetCurrentProcess(), &ct, &et, &st, &ut); - *utime = filetime_msecs(&ut); - *stime = filetime_msecs(&st); - PROCESS_MEMORY_COUNTERS info; - GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); - *current_rss = (size_t)info.WorkingSetSize; - *peak_rss = (size_t)info.PeakWorkingSetSize; - *current_commit = (size_t)info.PagefileUsage; - *peak_commit = (size_t)info.PeakPagefileUsage; - *page_faults = (size_t)info.PageFaultCount; -} - -#elif defined(__unix__) || defined(__unix) || defined(unix) || defined(__APPLE__) || defined(__HAIKU__) -#include -#include -#include - -#if defined(__APPLE__) -#include -#endif - -#if defined(__HAIKU__) -#include -#endif - -static mi_msecs_t timeval_secs(const struct timeval* tv) { - return ((mi_msecs_t)tv->tv_sec * 1000L) + ((mi_msecs_t)tv->tv_usec / 1000L); -} - -static void mi_stat_process_info(mi_msecs_t* elapsed, mi_msecs_t* utime, mi_msecs_t* stime, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults) -{ - *elapsed = _mi_clock_end(mi_process_start); - struct rusage rusage; - getrusage(RUSAGE_SELF, &rusage); - *utime = timeval_secs(&rusage.ru_utime); - *stime = timeval_secs(&rusage.ru_stime); -#if !defined(__HAIKU__) - *page_faults = rusage.ru_majflt; -#endif - // estimate commit using our stats - *peak_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.peak)); - *current_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.current)); - *current_rss = *current_commit; // estimate -#if defined(__HAIKU__) - // Haiku does not have (yet?) a way to - // get these stats per process - thread_info tid; - area_info mem; - ssize_t c; - get_thread_info(find_thread(0), &tid); - while (get_next_area_info(tid.team, &c, &mem) == B_OK) { - *peak_rss += mem.ram_size; - } -#elif defined(__APPLE__) - *peak_rss = rusage.ru_maxrss; // BSD reports in bytes - struct mach_task_basic_info info; - mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; - if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount) == KERN_SUCCESS) { - *current_rss = (size_t)info.resident_size; - } -#else - *peak_rss = rusage.ru_maxrss * 1024; // Linux reports in KiB -#endif -} - -#else -#ifndef __wasi__ -// WebAssembly instances are not processes -#pragma message("define a way to get process info") -#endif - -static void mi_stat_process_info(mi_msecs_t* elapsed, mi_msecs_t* utime, mi_msecs_t* stime, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults) -{ - *elapsed = _mi_clock_end(mi_process_start); - *peak_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.peak)); - *current_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.current)); - *peak_rss = *peak_commit; - *current_rss = *current_commit; - *page_faults = 0; - *utime = 0; - *stime = 0; -} -#endif - - mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, size_t* system_msecs, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults) mi_attr_noexcept { - mi_msecs_t elapsed = 0; - mi_msecs_t utime = 0; - mi_msecs_t stime = 0; - size_t current_rss0 = 0; - size_t peak_rss0 = 0; - size_t current_commit0 = 0; - size_t peak_commit0 = 0; - size_t page_faults0 = 0; - mi_stat_process_info(&elapsed,&utime, &stime, ¤t_rss0, &peak_rss0, ¤t_commit0, &peak_commit0, &page_faults0); - if (elapsed_msecs!=NULL) *elapsed_msecs = (elapsed < 0 ? 0 : (elapsed < (mi_msecs_t)PTRDIFF_MAX ? (size_t)elapsed : PTRDIFF_MAX)); - if (user_msecs!=NULL) *user_msecs = (utime < 0 ? 0 : (utime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)utime : PTRDIFF_MAX)); - if (system_msecs!=NULL) *system_msecs = (stime < 0 ? 0 : (stime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)stime : PTRDIFF_MAX)); - if (current_rss!=NULL) *current_rss = current_rss0; - if (peak_rss!=NULL) *peak_rss = peak_rss0; - if (current_commit!=NULL) *current_commit = current_commit0; - if (peak_commit!=NULL) *peak_commit = peak_commit0; - if (page_faults!=NULL) *page_faults = page_faults0; + mi_process_info_t pinfo; + _mi_memzero(&pinfo,sizeof(pinfo)); + pinfo.elapsed = _mi_clock_end(mi_process_start); + pinfo.current_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.current)); + pinfo.peak_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.peak)); + pinfo.current_rss = pinfo.current_commit; + pinfo.peak_rss = pinfo.peak_commit; + pinfo.utime = 0; + pinfo.stime = 0; + pinfo.page_faults = 0; + + _mi_prim_process_info(&pinfo); + + if (elapsed_msecs!=NULL) *elapsed_msecs = (pinfo.elapsed < 0 ? 0 : (pinfo.elapsed < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.elapsed : PTRDIFF_MAX)); + if (user_msecs!=NULL) *user_msecs = (pinfo.utime < 0 ? 0 : (pinfo.utime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.utime : PTRDIFF_MAX)); + if (system_msecs!=NULL) *system_msecs = (pinfo.stime < 0 ? 0 : (pinfo.stime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.stime : PTRDIFF_MAX)); + if (current_rss!=NULL) *current_rss = pinfo.current_rss; + if (peak_rss!=NULL) *peak_rss = pinfo.peak_rss; + if (current_commit!=NULL) *current_commit = pinfo.current_commit; + if (peak_commit!=NULL) *peak_commit = pinfo.peak_commit; + if (page_faults!=NULL) *page_faults = pinfo.page_faults; } - diff --git a/Source/mimalloc/test/CMakeLists.txt b/Source/mimalloc/test/CMakeLists.txt index 7986d2da1..e76ffa64f 100644 --- a/Source/mimalloc/test/CMakeLists.txt +++ b/Source/mimalloc/test/CMakeLists.txt @@ -1,6 +1,9 @@ cmake_minimum_required(VERSION 3.0) project(mimalloc-test C CXX) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + # Set default build type if (NOT CMAKE_BUILD_TYPE) if ("${CMAKE_BINARY_DIR}" MATCHES ".*(D|d)ebug$") @@ -13,8 +16,8 @@ if (NOT CMAKE_BUILD_TYPE) endif() # Import mimalloc (if installed) -find_package(mimalloc 1.7 REQUIRED NO_SYSTEM_ENVIRONMENT_PATH) -message(STATUS "Found mimalloc installed at: ${MIMALLOC_LIBRARY_DIR}") +find_package(mimalloc 2.0 REQUIRED NO_SYSTEM_ENVIRONMENT_PATH) +message(STATUS "Found mimalloc installed at: ${MIMALLOC_LIBRARY_DIR} (${MIMALLOC_VERSION_DIR})") # overriding with a dynamic library add_executable(dynamic-override main-override.c) @@ -26,7 +29,7 @@ target_link_libraries(dynamic-override-cxx PUBLIC mimalloc) # overriding with a static object file works reliable as the symbols in the # object file have priority over those in library files -add_executable(static-override-obj main-override.c ${MIMALLOC_LIBRARY_DIR}/mimalloc.o) +add_executable(static-override-obj main-override.c ${MIMALLOC_OBJECT_DIR}/mimalloc.o) target_include_directories(static-override-obj PUBLIC ${MIMALLOC_INCLUDE_DIR}) target_link_libraries(static-override-obj PUBLIC pthread) @@ -44,3 +47,8 @@ target_link_libraries(static-override PUBLIC mimalloc-static) add_executable(static-override-cxx main-override.cpp) target_link_libraries(static-override-cxx PUBLIC mimalloc-static) + + +## test memory errors +add_executable(test-wrong test-wrong.c) +target_link_libraries(test-wrong PUBLIC mimalloc) diff --git a/Source/mimalloc/test/main-override-static.c b/Source/mimalloc/test/main-override-static.c index 221db7e86..e71be29e9 100644 --- a/Source/mimalloc/test/main-override-static.c +++ b/Source/mimalloc/test/main-override-static.c @@ -7,6 +7,7 @@ #include #include // redefines malloc etc. + static void double_free1(); static void double_free2(); static void corrupt_free(); @@ -16,6 +17,10 @@ static void test_aslr(void); static void test_process_info(void); static void test_reserved(void); static void negative_stat(void); +static void alloc_huge(void); +static void test_heap_walk(void); +static void test_heap_arena(void); +static void test_align(void); int main() { mi_version(); @@ -29,6 +34,11 @@ int main() { // invalid_free(); // test_reserved(); // negative_stat(); + // test_heap_walk(); + // alloc_huge(); + // test_heap_walk(); + // test_heap_arena(); + // test_align(); void* p1 = malloc(78); void* p2 = malloc(24); @@ -36,7 +46,10 @@ int main() { p1 = mi_malloc(8); char* s = strdup("hello\n"); free(p2); - + + mi_heap_t* h = mi_heap_new(); + mi_heap_set_default(h); + p2 = malloc(16); p1 = realloc(p1, 32); free(p1); @@ -48,12 +61,22 @@ int main() { //free(p1); //p2 = malloc(32); //mi_free(p2); - mi_collect(true); - mi_stats_print(NULL); + + //mi_collect(true); + //mi_stats_print(NULL); + // test_process_info(); + return 0; } +static void test_align() { + void* p = mi_malloc_aligned(256, 256); + if (((uintptr_t)p % 256) != 0) { + fprintf(stderr, "%p is not 256 alignend!\n", p); + } +} + static void invalid_free() { free((void*)0xBADBEEF); realloc((void*)0xBADBEEF,10); @@ -146,7 +169,7 @@ static void test_process_info(void) { size_t peak_rss = 0; size_t current_commit = 0; size_t peak_commit = 0; - size_t page_faults = 0; + size_t page_faults = 0; for (int i = 0; i < 100000; i++) { void* p = calloc(100,10); free(p); @@ -178,5 +201,215 @@ static void negative_stat(void) { mi_stats_print_out(NULL, NULL); *p = 100; mi_free(p); - mi_stats_print_out(NULL, NULL); -} \ No newline at end of file + mi_stats_print_out(NULL, NULL); +} + +static void alloc_huge(void) { + void* p = mi_malloc(67108872); + mi_free(p); +} + +static bool test_visit(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg) { + if (block == NULL) { + printf("visiting an area with blocks of size %zu (including padding)\n", area->full_block_size); + } + else { + printf(" block of size %zu (allocated size is %zu)\n", block_size, mi_usable_size(block)); + } + return true; +} + +static void test_heap_walk(void) { + mi_heap_t* heap = mi_heap_new(); + mi_heap_malloc(heap, 16*2097152); + mi_heap_malloc(heap, 2067152); + mi_heap_malloc(heap, 2097160); + mi_heap_malloc(heap, 24576); + mi_heap_visit_blocks(heap, true, &test_visit, NULL); +} + +static void test_heap_arena(void) { + mi_arena_id_t arena_id; + int err = mi_reserve_os_memory_ex(100 * 1024 * 1024, false /* commit */, false /* allow large */, true /* exclusive */, &arena_id); + if (err) abort(); + mi_heap_t* heap = mi_heap_new_in_arena(arena_id); + for (int i = 0; i < 500000; i++) { + void* p = mi_heap_malloc(heap, 1024); + if (p == NULL) { + printf("out of memory after %d kb (expecting about 100_000kb)\n", i); + break; + } + } +} + +// ---------------------------- +// bin size experiments +// ------------------------------ + +#if 0 +#include +#include + +#define MI_INTPTR_SIZE 8 +#define MI_LARGE_WSIZE_MAX (4*1024*1024 / MI_INTPTR_SIZE) + +#define MI_BIN_HUGE 100 +//#define MI_ALIGN2W + +// Bit scan reverse: return the index of the highest bit. +static inline uint8_t mi_bsr32(uint32_t x); + +#if defined(_MSC_VER) +#include +#include +static inline uint8_t mi_bsr32(uint32_t x) { + uint32_t idx; + _BitScanReverse((DWORD*)&idx, x); + return idx; +} +#elif defined(__GNUC__) || defined(__clang__) +static inline uint8_t mi_bsr32(uint32_t x) { + return (31 - __builtin_clz(x)); +} +#else +static inline uint8_t mi_bsr32(uint32_t x) { + // de Bruijn multiplication, see + static const uint8_t debruijn[32] = { + 31, 0, 22, 1, 28, 23, 18, 2, 29, 26, 24, 10, 19, 7, 3, 12, + 30, 21, 27, 17, 25, 9, 6, 11, 20, 16, 8, 5, 15, 4, 14, 13, + }; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x++; + return debruijn[(x*0x076be629) >> 27]; +} +#endif + +/* +// Bit scan reverse: return the index of the highest bit. +uint8_t _mi_bsr(uintptr_t x) { + if (x == 0) return 0; + #if MI_INTPTR_SIZE==8 + uint32_t hi = (x >> 32); + return (hi == 0 ? mi_bsr32((uint32_t)x) : 32 + mi_bsr32(hi)); + #elif MI_INTPTR_SIZE==4 + return mi_bsr32(x); + #else + # error "define bsr for non-32 or 64-bit platforms" + #endif +} +*/ + + +static inline size_t _mi_wsize_from_size(size_t size) { + return (size + sizeof(uintptr_t) - 1) / sizeof(uintptr_t); +} + +// Return the bin for a given field size. +// Returns MI_BIN_HUGE if the size is too large. +// We use `wsize` for the size in "machine word sizes", +// i.e. byte size == `wsize*sizeof(void*)`. +extern inline uint8_t _mi_bin8(size_t size) { + size_t wsize = _mi_wsize_from_size(size); + uint8_t bin; + if (wsize <= 1) { + bin = 1; + } +#if defined(MI_ALIGN4W) + else if (wsize <= 4) { + bin = (uint8_t)((wsize+1)&~1); // round to double word sizes + } +#elif defined(MI_ALIGN2W) + else if (wsize <= 8) { + bin = (uint8_t)((wsize+1)&~1); // round to double word sizes + } +#else + else if (wsize <= 8) { + bin = (uint8_t)wsize; + } +#endif + else if (wsize > MI_LARGE_WSIZE_MAX) { + bin = MI_BIN_HUGE; + } + else { +#if defined(MI_ALIGN4W) + if (wsize <= 16) { wsize = (wsize+3)&~3; } // round to 4x word sizes +#endif + wsize--; + // find the highest bit + uint8_t b = mi_bsr32((uint32_t)wsize); + // and use the top 3 bits to determine the bin (~12.5% worst internal fragmentation). + // - adjust with 3 because we use do not round the first 8 sizes + // which each get an exact bin + bin = ((b << 2) + (uint8_t)((wsize >> (b - 2)) & 0x03)) - 3; + } + return bin; +} + +static inline uint8_t _mi_bin4(size_t size) { + size_t wsize = _mi_wsize_from_size(size); + uint8_t bin; + if (wsize <= 1) { + bin = 1; + } +#if defined(MI_ALIGN4W) + else if (wsize <= 4) { + bin = (uint8_t)((wsize+1)&~1); // round to double word sizes + } +#elif defined(MI_ALIGN2W) + else if (wsize <= 8) { + bin = (uint8_t)((wsize+1)&~1); // round to double word sizes + } +#else + else if (wsize <= 8) { + bin = (uint8_t)wsize; + } +#endif + else if (wsize > MI_LARGE_WSIZE_MAX) { + bin = MI_BIN_HUGE; + } + else { + uint8_t b = mi_bsr32((uint32_t)wsize); + bin = ((b << 1) + (uint8_t)((wsize >> (b - 1)) & 0x01)) + 3; + } + return bin; +} + +static size_t _mi_binx4(size_t bsize) { + if (bsize==0) return 0; + uint8_t b = mi_bsr32((uint32_t)bsize); + if (b <= 1) return bsize; + size_t bin = ((b << 1) | (bsize >> (b - 1))&0x01); + return bin; +} + +static size_t _mi_binx8(size_t bsize) { + if (bsize<=1) return bsize; + uint8_t b = mi_bsr32((uint32_t)bsize); + if (b <= 2) return bsize; + size_t bin = ((b << 2) | (bsize >> (b - 2))&0x03) - 5; + return bin; +} + +static void mi_bins(void) { + //printf(" QNULL(1), /* 0 */ \\\n "); + size_t last_bin = 0; + size_t min_bsize = 0; + size_t last_bsize = 0; + for (size_t bsize = 1; bsize < 2*1024; bsize++) { + size_t size = bsize * 64 * 1024; + size_t bin = _mi_binx8(bsize); + if (bin != last_bin) { + printf("min bsize: %6zd, max bsize: %6zd, bin: %6zd\n", min_bsize, last_bsize, last_bin); + //printf("QNULL(%6zd), ", wsize); + //if (last_bin%8 == 0) printf("/* %i */ \\\n ", last_bin); + last_bin = bin; + min_bsize = bsize; + } + last_bsize = bsize; + } +} +#endif diff --git a/Source/mimalloc/test/main-override.c b/Source/mimalloc/test/main-override.c index 1bec11797..284fdd204 100644 --- a/Source/mimalloc/test/main-override.c +++ b/Source/mimalloc/test/main-override.c @@ -3,7 +3,7 @@ #include #include -#include +#include int main() { mi_version(); // ensure mimalloc library is linked @@ -25,6 +25,12 @@ int main() { //free(p1); //p2 = malloc(32); //mi_free(p2); + p1 = malloc(24); + p2 = reallocarray(p1, 16, 16); + free(p2); + p1 = malloc(24); + assert(reallocarr(&p1, 16, 16) == 0); + free(p1); mi_stats_print(NULL); return 0; } diff --git a/Source/mimalloc/test/main-override.cpp b/Source/mimalloc/test/main-override.cpp index 5fe3f9a31..c1607b667 100644 --- a/Source/mimalloc/test/main-override.cpp +++ b/Source/mimalloc/test/main-override.cpp @@ -26,27 +26,47 @@ static void msleep(unsigned long msecs) { Sleep(msecs); } static void msleep(unsigned long msecs) { usleep(msecs * 1000UL); } #endif -void heap_thread_free_large(); // issue #221 -void heap_no_delete(); // issue #202 -void heap_late_free(); // issue #204 -void padding_shrink(); // issue #209 -void various_tests(); -void test_mt_shutdown(); -void fail_aslr(); // issue #372 -void tsan_numa_test(); // issue #414 +static void heap_thread_free_large(); // issue #221 +static void heap_no_delete(); // issue #202 +static void heap_late_free(); // issue #204 +static void padding_shrink(); // issue #209 +static void various_tests(); +static void test_mt_shutdown(); +static void large_alloc(void); // issue #363 +static void fail_aslr(); // issue #372 +static void tsan_numa_test(); // issue #414 +static void strdup_test(); // issue #445 +static void bench_alloc_large(void); // issue #xxx +//static void test_large_migrate(void); // issue #691 +static void heap_thread_free_huge(); +static void test_std_string(); // issue #697 + +static void test_stl_allocators(); + int main() { - mi_stats_reset(); // ignore earlier allocations - heap_thread_free_large(); - heap_no_delete(); - heap_late_free(); - padding_shrink(); - various_tests(); - tsan_numa_test(); - - //test_mt_shutdown(); + // mi_stats_reset(); // ignore earlier allocations + + // test_std_string(); + // heap_thread_free_huge(); + /* + heap_thread_free_huge(); + heap_thread_free_large(); + heap_no_delete(); + heap_late_free(); + padding_shrink(); + various_tests(); + large_alloc(); + tsan_numa_test(); + strdup_test(); + */ + // test_stl_allocators(); + // test_mt_shutdown(); + // test_large_migrate(); + //fail_aslr(); - mi_stats_print(NULL); + // bench_alloc_large(); + // mi_stats_print(NULL); return 0; } @@ -66,30 +86,29 @@ class Test { }; -void various_tests() { +static void various_tests() { atexit(free_p); void* p1 = malloc(78); - void* p2 = mi_malloc_aligned(16, 24); + void* p2 = mi_malloc_aligned(24, 16); free(p1); p1 = malloc(8); char* s = mi_strdup("hello\n"); - //char* s = _strdup("hello\n"); - //char* buf = NULL; - //size_t len; - //_dupenv_s(&buf,&len,"MIMALLOC_VERBOSE"); - //mi_free(buf); - mi_free(p2); p2 = malloc(16); p1 = realloc(p1, 32); free(p1); free(p2); mi_free(s); + Test* t = new Test(42); delete t; t = new (std::nothrow) Test(42); delete t; + auto tbuf = new unsigned char[sizeof(Test)]; + t = new (tbuf) Test(42); + t->~Test(); + delete tbuf; } class Static { @@ -109,7 +128,7 @@ class Static { static Static s = Static(); -bool test_stl_allocator1() { +static bool test_stl_allocator1() { std::vector > vec; vec.push_back(1); vec.pop_back(); @@ -118,38 +137,132 @@ bool test_stl_allocator1() { struct some_struct { int i; int j; double z; }; -bool test_stl_allocator2() { +static bool test_stl_allocator2() { std::vector > vec; vec.push_back(some_struct()); vec.pop_back(); return vec.size() == 0; } +#if MI_HAS_HEAP_STL_ALLOCATOR +static bool test_stl_allocator3() { + std::vector > vec; + vec.push_back(1); + vec.pop_back(); + return vec.size() == 0; +} + +static bool test_stl_allocator4() { + std::vector > vec; + vec.push_back(some_struct()); + vec.pop_back(); + return vec.size() == 0; +} + +static bool test_stl_allocator5() { + std::vector > vec; + vec.push_back(1); + vec.pop_back(); + return vec.size() == 0; +} + +static bool test_stl_allocator6() { + std::vector > vec; + vec.push_back(some_struct()); + vec.pop_back(); + return vec.size() == 0; +} +#endif + +static void test_stl_allocators() { + test_stl_allocator1(); + test_stl_allocator2(); +#if MI_HAS_HEAP_STL_ALLOCATOR + test_stl_allocator3(); + test_stl_allocator4(); + test_stl_allocator5(); + test_stl_allocator6(); +#endif +} + +#if 0 +// issue #691 +static char* cptr; + +static void* thread1_allocate() +{ + cptr = mi_calloc_tp(char,22085632); + return NULL; +} + +static void* thread2_free() +{ + assert(cptr); + mi_free(cptr); + cptr = NULL; + return NULL; +} + +static void test_large_migrate(void) { + auto t1 = std::thread(thread1_allocate); + t1.join(); + auto t2 = std::thread(thread2_free); + t2.join(); + /* + pthread_t thread1, thread2; + + pthread_create(&thread1, NULL, &thread1_allocate, NULL); + pthread_join(thread1, NULL); + + pthread_create(&thread2, NULL, &thread2_free, NULL); + pthread_join(thread2, NULL); + */ + return; +} +#endif +// issue 445 +static void strdup_test() { +#ifdef _MSC_VER + char* s = _strdup("hello\n"); + char* buf = NULL; + size_t len; + _dupenv_s(&buf, &len, "MIMALLOC_VERBOSE"); + mi_free(buf); + mi_free(s); +#endif +} // Issue #202 -void heap_no_delete_worker() { +static void heap_no_delete_worker() { mi_heap_t* heap = mi_heap_new(); - void* q = mi_heap_malloc(heap, 1024); + void* q = mi_heap_malloc(heap, 1024); (void)(q); // mi_heap_delete(heap); // uncomment to prevent assertion } -void heap_no_delete() { +static void heap_no_delete() { auto t1 = std::thread(heap_no_delete_worker); t1.join(); } +// Issue #697 +static void test_std_string() { + std::string path = "/Users/xxxx/Library/Developer/Xcode/DerivedData/xxxxxxxxxx/Build/Intermediates.noindex/xxxxxxxxxxx/arm64/XX_lto.o/0.arm64.lto.o"; + std::string path1 = "/Users/xxxx/Library/Developer/Xcode/DerivedData/xxxxxxxxxx/Build/Intermediates.noindex/xxxxxxxxxxx/arm64/XX_lto.o/1.arm64.lto.o"; + std::cout << path + "\n>>> " + path1 + "\n>>> " << std::endl; +} + // Issue #204 -volatile void* global_p; +static volatile void* global_p; -void t1main() { +static void t1main() { mi_heap_t* heap = mi_heap_new(); global_p = mi_heap_malloc(heap, 1024); mi_heap_delete(heap); } -void heap_late_free() { +static void heap_late_free() { auto t1 = std::thread(t1main); msleep(2000); @@ -166,7 +279,7 @@ static void alloc0(/* void* arg */) shared_p = mi_malloc(8); } -void padding_shrink(void) +static void padding_shrink(void) { auto t1 = std::thread(alloc0); t1.join(); @@ -175,21 +288,31 @@ void padding_shrink(void) // Issue #221 -void heap_thread_free_large_worker() { +static void heap_thread_free_large_worker() { mi_free(shared_p); } -void heap_thread_free_large() { +static void heap_thread_free_large() { for (int i = 0; i < 100; i++) { - shared_p = mi_malloc_aligned(2*1024*1024 + 1, 8); + shared_p = mi_malloc_aligned(2 * 1024 * 1024 + 1, 8); auto t1 = std::thread(heap_thread_free_large_worker); t1.join(); } } +static void heap_thread_free_huge_worker() { + mi_free(shared_p); +} +static void heap_thread_free_huge() { + for (int i = 0; i < 100; i++) { + shared_p = mi_malloc(1024 * 1024 * 1024); + auto t1 = std::thread(heap_thread_free_huge_worker); + t1.join(); + } +} -void test_mt_shutdown() +static void test_mt_shutdown() { const int threads = 5; std::vector< std::future< std::vector< char* > > > ts; @@ -213,8 +336,20 @@ void test_mt_shutdown() std::cout << "done" << std::endl; } +// issue #363 +using namespace std; + +void large_alloc(void) +{ + char* a = new char[1ull << 25]; + thread th([&] { + delete[] a; + }); + th.join(); +} + // issue #372 -void fail_aslr() { +static void fail_aslr() { size_t sz = (4ULL << 40); // 4TiB void* p = malloc(sz); printf("pointer p: %p: area up to %p\n", p, (uint8_t*)p + sz); @@ -222,13 +357,44 @@ void fail_aslr() { } // issues #414 -void dummy_worker() { +static void dummy_worker() { void* p = mi_malloc(0); - mi_free(p); + mi_free(p); } -void tsan_numa_test() { +static void tsan_numa_test() { auto t1 = std::thread(dummy_worker); dummy_worker(); t1.join(); -} \ No newline at end of file +} + +// issue #? +#include +#include +#include + +static void bench_alloc_large(void) { + static constexpr int kNumBuffers = 20; + static constexpr size_t kMinBufferSize = 5 * 1024 * 1024; + static constexpr size_t kMaxBufferSize = 25 * 1024 * 1024; + std::unique_ptr buffers[kNumBuffers]; + + std::random_device rd; + std::mt19937 gen(42); //rd()); + std::uniform_int_distribution<> size_distribution(kMinBufferSize, kMaxBufferSize); + std::uniform_int_distribution<> buf_number_distribution(0, kNumBuffers - 1); + + static constexpr int kNumIterations = 2000; + const auto start = std::chrono::steady_clock::now(); + for (int i = 0; i < kNumIterations; ++i) { + int buffer_idx = buf_number_distribution(gen); + size_t new_size = size_distribution(gen); + buffers[buffer_idx] = std::make_unique(new_size); + } + const auto end = std::chrono::steady_clock::now(); + const auto num_ms = std::chrono::duration_cast(end - start).count(); + const auto us_per_allocation = std::chrono::duration_cast(end - start).count() / kNumIterations; + std::cout << kNumIterations << " allocations Done in " << num_ms << "ms." << std::endl; + std::cout << "Avg " << us_per_allocation << " us per allocation" << std::endl; +} + diff --git a/Source/mimalloc/test/test-api-fill.c b/Source/mimalloc/test/test-api-fill.c new file mode 100644 index 000000000..3fca3b9d4 --- /dev/null +++ b/Source/mimalloc/test/test-api-fill.c @@ -0,0 +1,343 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2020, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ +#include "mimalloc.h" +#include "mimalloc/types.h" + +#include "testhelper.h" + +// --------------------------------------------------------------------------- +// Helper functions +// --------------------------------------------------------------------------- +bool check_zero_init(uint8_t* p, size_t size); +#if MI_DEBUG >= 2 +bool check_debug_fill_uninit(uint8_t* p, size_t size); +bool check_debug_fill_freed(uint8_t* p, size_t size); +#endif + +// --------------------------------------------------------------------------- +// Main testing +// --------------------------------------------------------------------------- +int main(void) { + mi_option_disable(mi_option_verbose); + + // --------------------------------------------------- + // Zeroing allocation + // --------------------------------------------------- + CHECK_BODY("zeroinit-zalloc-small") { + size_t zalloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_zalloc(zalloc_size); + result = check_zero_init(p, zalloc_size); + mi_free(p); + }; + CHECK_BODY("zeroinit-zalloc-large") { + size_t zalloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_zalloc(zalloc_size); + result = check_zero_init(p, zalloc_size); + mi_free(p); + }; + CHECK_BODY("zeroinit-zalloc_small") { + size_t zalloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_zalloc_small(zalloc_size); + result = check_zero_init(p, zalloc_size); + mi_free(p); + }; + + CHECK_BODY("zeroinit-calloc-small") { + size_t calloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_calloc(calloc_size, 1); + result = check_zero_init(p, calloc_size); + mi_free(p); + }; + CHECK_BODY("zeroinit-calloc-large") { + size_t calloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_calloc(calloc_size, 1); + result = check_zero_init(p, calloc_size); + mi_free(p); + }; + + CHECK_BODY("zeroinit-rezalloc-small") { + size_t zalloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_zalloc(zalloc_size); + result = check_zero_init(p, zalloc_size); + zalloc_size *= 3; + p = (uint8_t*)mi_rezalloc(p, zalloc_size); + result &= check_zero_init(p, zalloc_size); + mi_free(p); + }; + CHECK_BODY("zeroinit-rezalloc-large") { + size_t zalloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_zalloc(zalloc_size); + result = check_zero_init(p, zalloc_size); + zalloc_size *= 3; + p = (uint8_t*)mi_rezalloc(p, zalloc_size); + result &= check_zero_init(p, zalloc_size); + mi_free(p); + }; + + CHECK_BODY("zeroinit-recalloc-small") { + size_t calloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_calloc(calloc_size, 1); + result = check_zero_init(p, calloc_size); + calloc_size *= 3; + p = (uint8_t*)mi_recalloc(p, calloc_size, 1); + result &= check_zero_init(p, calloc_size); + mi_free(p); + }; + CHECK_BODY("zeroinit-recalloc-large") { + size_t calloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_calloc(calloc_size, 1); + result = check_zero_init(p, calloc_size); + calloc_size *= 3; + p = (uint8_t*)mi_recalloc(p, calloc_size, 1); + result &= check_zero_init(p, calloc_size); + mi_free(p); + }; + + // --------------------------------------------------- + // Zeroing in aligned API + // --------------------------------------------------- + CHECK_BODY("zeroinit-zalloc_aligned-small") { + size_t zalloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_zalloc_aligned(zalloc_size, MI_MAX_ALIGN_SIZE * 2); + result = check_zero_init(p, zalloc_size); + mi_free(p); + }; + CHECK_BODY("zeroinit-zalloc_aligned-large") { + size_t zalloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_zalloc_aligned(zalloc_size, MI_MAX_ALIGN_SIZE * 2); + result = check_zero_init(p, zalloc_size); + mi_free(p); + }; + + CHECK_BODY("zeroinit-calloc_aligned-small") { + size_t calloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_calloc_aligned(calloc_size, 1, MI_MAX_ALIGN_SIZE * 2); + result = check_zero_init(p, calloc_size); + mi_free(p); + }; + CHECK_BODY("zeroinit-calloc_aligned-large") { + size_t calloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_calloc_aligned(calloc_size, 1, MI_MAX_ALIGN_SIZE * 2); + result = check_zero_init(p, calloc_size); + mi_free(p); + }; + + CHECK_BODY("zeroinit-rezalloc_aligned-small") { + size_t zalloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_zalloc_aligned(zalloc_size, MI_MAX_ALIGN_SIZE * 2); + result = check_zero_init(p, zalloc_size); + zalloc_size *= 3; + p = (uint8_t*)mi_rezalloc_aligned(p, zalloc_size, MI_MAX_ALIGN_SIZE * 2); + result &= check_zero_init(p, zalloc_size); + mi_free(p); + }; + CHECK_BODY("zeroinit-rezalloc_aligned-large") { + size_t zalloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_zalloc_aligned(zalloc_size, MI_MAX_ALIGN_SIZE * 2); + result = check_zero_init(p, zalloc_size); + zalloc_size *= 3; + p = (uint8_t*)mi_rezalloc_aligned(p, zalloc_size, MI_MAX_ALIGN_SIZE * 2); + result &= check_zero_init(p, zalloc_size); + mi_free(p); + }; + + CHECK_BODY("zeroinit-recalloc_aligned-small") { + size_t calloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_calloc_aligned(calloc_size, 1, MI_MAX_ALIGN_SIZE * 2); + result = check_zero_init(p, calloc_size); + calloc_size *= 3; + p = (uint8_t*)mi_recalloc_aligned(p, calloc_size, 1, MI_MAX_ALIGN_SIZE * 2); + result &= check_zero_init(p, calloc_size); + mi_free(p); + }; + CHECK_BODY("zeroinit-recalloc_aligned-large") { + size_t calloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_calloc_aligned(calloc_size, 1, MI_MAX_ALIGN_SIZE * 2); + result = check_zero_init(p, calloc_size); + calloc_size *= 3; + p = (uint8_t*)mi_recalloc_aligned(p, calloc_size, 1, MI_MAX_ALIGN_SIZE * 2); + result &= check_zero_init(p, calloc_size); + mi_free(p); + }; + +#if (MI_DEBUG >= 2) && !MI_TSAN + // --------------------------------------------------- + // Debug filling + // --------------------------------------------------- + CHECK_BODY("uninit-malloc-small") { + size_t malloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_malloc(malloc_size); + result = check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + CHECK_BODY("uninit-malloc-large") { + size_t malloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_malloc(malloc_size); + result = check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + + CHECK_BODY("uninit-malloc_small") { + size_t malloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_malloc_small(malloc_size); + result = check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + + CHECK_BODY("uninit-realloc-small") { + size_t malloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_malloc(malloc_size); + result = check_debug_fill_uninit(p, malloc_size); + malloc_size *= 3; + p = (uint8_t*)mi_realloc(p, malloc_size); + result &= check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + CHECK_BODY("uninit-realloc-large") { + size_t malloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_malloc(malloc_size); + result = check_debug_fill_uninit(p, malloc_size); + malloc_size *= 3; + p = (uint8_t*)mi_realloc(p, malloc_size); + result &= check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + + CHECK_BODY("uninit-mallocn-small") { + size_t malloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_mallocn(malloc_size, 1); + result = check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + CHECK_BODY("uninit-mallocn-large") { + size_t malloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_mallocn(malloc_size, 1); + result = check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + + CHECK_BODY("uninit-reallocn-small") { + size_t malloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_mallocn(malloc_size, 1); + result = check_debug_fill_uninit(p, malloc_size); + malloc_size *= 3; + p = (uint8_t*)mi_reallocn(p, malloc_size, 1); + result &= check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + CHECK_BODY("uninit-reallocn-large") { + size_t malloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_mallocn(malloc_size, 1); + result = check_debug_fill_uninit(p, malloc_size); + malloc_size *= 3; + p = (uint8_t*)mi_reallocn(p, malloc_size, 1); + result &= check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + + CHECK_BODY("uninit-malloc_aligned-small") { + size_t malloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_malloc_aligned(malloc_size, MI_MAX_ALIGN_SIZE * 2); + result = check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + CHECK_BODY("uninit-malloc_aligned-large") { + size_t malloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_malloc_aligned(malloc_size, MI_MAX_ALIGN_SIZE * 2); + result = check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + + CHECK_BODY("uninit-realloc_aligned-small") { + size_t malloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_malloc_aligned(malloc_size, MI_MAX_ALIGN_SIZE * 2); + result = check_debug_fill_uninit(p, malloc_size); + malloc_size *= 3; + p = (uint8_t*)mi_realloc_aligned(p, malloc_size, MI_MAX_ALIGN_SIZE * 2); + result &= check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + CHECK_BODY("uninit-realloc_aligned-large") { + size_t malloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_malloc_aligned(malloc_size, MI_MAX_ALIGN_SIZE * 2); + result = check_debug_fill_uninit(p, malloc_size); + malloc_size *= 3; + p = (uint8_t*)mi_realloc_aligned(p, malloc_size, MI_MAX_ALIGN_SIZE * 2); + result &= check_debug_fill_uninit(p, malloc_size); + mi_free(p); + }; + + #if !(MI_TRACK_VALGRIND || MI_TRACK_ASAN) + CHECK_BODY("fill-freed-small") { + size_t malloc_size = MI_SMALL_SIZE_MAX / 2; + uint8_t* p = (uint8_t*)mi_malloc(malloc_size); + mi_free(p); + // First sizeof(void*) bytes will contain housekeeping data, skip these + result = check_debug_fill_freed(p + sizeof(void*), malloc_size - sizeof(void*)); + }; + CHECK_BODY("fill-freed-large") { + size_t malloc_size = MI_SMALL_SIZE_MAX * 2; + uint8_t* p = (uint8_t*)mi_malloc(malloc_size); + mi_free(p); + // First sizeof(void*) bytes will contain housekeeping data, skip these + result = check_debug_fill_freed(p + sizeof(void*), malloc_size - sizeof(void*)); + }; + #endif +#endif + + // --------------------------------------------------- + // Done + // ---------------------------------------------------[] + return print_test_summary(); +} + +// --------------------------------------------------------------------------- +// Helper functions +// --------------------------------------------------------------------------- +bool check_zero_init(uint8_t* p, size_t size) { + if(!p) + return false; + bool result = true; + for (size_t i = 0; i < size; ++i) { + result &= p[i] == 0; + } + return result; +} + +#if MI_DEBUG >= 2 +bool check_debug_fill_uninit(uint8_t* p, size_t size) { +#if MI_TRACK_VALGRIND || MI_TRACK_ASAN + (void)p; (void)size; + return true; // when compiled with valgrind we don't init on purpose +#else + if(!p) + return false; + + bool result = true; + for (size_t i = 0; i < size; ++i) { + result &= p[i] == MI_DEBUG_UNINIT; + } + return result; +#endif +} + +bool check_debug_fill_freed(uint8_t* p, size_t size) { +#if MI_TRACK_VALGRIND + (void)p; (void)size; + return true; // when compiled with valgrind we don't fill on purpose +#else + if(!p) + return false; + + bool result = true; + for (size_t i = 0; i < size; ++i) { + result &= p[i] == MI_DEBUG_FREED; + } + return result; +#endif +} +#endif diff --git a/Source/mimalloc/test/test-api.c b/Source/mimalloc/test/test-api.c index d33449289..c78e19728 100644 --- a/Source/mimalloc/test/test-api.c +++ b/Source/mimalloc/test/test-api.c @@ -4,6 +4,9 @@ This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. -----------------------------------------------------------------------------*/ +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic ignored "-Walloc-size-larger-than=" +#endif /* Testing allocators is difficult as bugs may only surface after particular @@ -20,7 +23,6 @@ we therefore test the API over various inputs. Please add more tests :-) [1] https://github.com/daanx/mimalloc-bench */ -#include #include #include #include @@ -31,119 +33,102 @@ we therefore test the API over various inputs. Please add more tests :-) #endif #include "mimalloc.h" -// #include "mimalloc-internal.h" +// #include "mimalloc/internal.h" +#include "mimalloc/types.h" // for MI_DEBUG and MI_ALIGNMENT_MAX -// --------------------------------------------------------------------------- -// Test macros: CHECK(name,predicate) and CHECK_BODY(name,body) -// --------------------------------------------------------------------------- -static int ok = 0; -static int failed = 0; - -#define CHECK_BODY(name,body) \ - do { \ - fprintf(stderr,"test: %s... ", name ); \ - bool result = true; \ - do { body } while(false); \ - if (!(result)) { \ - failed++; \ - fprintf(stderr, \ - "\n FAILED: %s:%d:\n %s\n", \ - __FILE__, \ - __LINE__, \ - #body); \ - /* exit(1); */ \ - } \ - else { \ - ok++; \ - fprintf(stderr,"ok.\n"); \ - } \ - } while (false) - -#define CHECK(name,expr) CHECK_BODY(name,{ result = (expr); }) +#include "testhelper.h" // --------------------------------------------------------------------------- // Test functions // --------------------------------------------------------------------------- -bool test_heap1(); -bool test_heap2(); -bool test_stl_allocator1(); -bool test_stl_allocator2(); +bool test_heap1(void); +bool test_heap2(void); +bool test_stl_allocator1(void); +bool test_stl_allocator2(void); // --------------------------------------------------------------------------- // Main testing // --------------------------------------------------------------------------- -int main() { +int main(void) { mi_option_disable(mi_option_verbose); - + // --------------------------------------------------- // Malloc // --------------------------------------------------- - CHECK_BODY("malloc-zero",{ - void* p = mi_malloc(0); mi_free(p); - }); - CHECK_BODY("malloc-nomem1",{ - result = (mi_malloc(SIZE_MAX/2) == NULL); - }); - CHECK_BODY("malloc-null",{ + CHECK_BODY("malloc-zero") { + void* p = mi_malloc(0); + result = (p != NULL); + mi_free(p); + }; + CHECK_BODY("malloc-nomem1") { + result = (mi_malloc((size_t)PTRDIFF_MAX + (size_t)1) == NULL); + }; + CHECK_BODY("malloc-null") { mi_free(NULL); - }); - CHECK_BODY("calloc-overflow",{ + }; + CHECK_BODY("calloc-overflow") { // use (size_t)&mi_calloc to get some number without triggering compiler warnings result = (mi_calloc((size_t)&mi_calloc,SIZE_MAX/1000) == NULL); - }); - CHECK_BODY("calloc0",{ - result = (mi_usable_size(mi_calloc(0,1000)) <= 16); - }); + }; + CHECK_BODY("calloc0") { + void* p = mi_calloc(0,1000); + result = (mi_usable_size(p) <= 16); + mi_free(p); + }; + CHECK_BODY("malloc-large") { // see PR #544. + void* p = mi_malloc(67108872); + mi_free(p); + }; // --------------------------------------------------- // Extended - // --------------------------------------------------- - CHECK_BODY("posix_memalign1", { + // --------------------------------------------------- + CHECK_BODY("posix_memalign1") { void* p = &p; int err = mi_posix_memalign(&p, sizeof(void*), 32); result = ((err==0 && (uintptr_t)p % sizeof(void*) == 0) || p==&p); mi_free(p); - }); - CHECK_BODY("posix_memalign_no_align", { + }; + CHECK_BODY("posix_memalign_no_align") { void* p = &p; int err = mi_posix_memalign(&p, 3, 32); result = (err==EINVAL && p==&p); - }); - CHECK_BODY("posix_memalign_zero", { + }; + CHECK_BODY("posix_memalign_zero") { void* p = &p; int err = mi_posix_memalign(&p, sizeof(void*), 0); mi_free(p); result = (err==0); - }); - CHECK_BODY("posix_memalign_nopow2", { + }; + CHECK_BODY("posix_memalign_nopow2") { void* p = &p; int err = mi_posix_memalign(&p, 3*sizeof(void*), 32); result = (err==EINVAL && p==&p); - }); - CHECK_BODY("posix_memalign_nomem", { + }; + CHECK_BODY("posix_memalign_nomem") { void* p = &p; int err = mi_posix_memalign(&p, sizeof(void*), SIZE_MAX); result = (err==ENOMEM && p==&p); - }); + }; // --------------------------------------------------- // Aligned API // --------------------------------------------------- - CHECK_BODY("malloc-aligned1", { + CHECK_BODY("malloc-aligned1") { void* p = mi_malloc_aligned(32,32); result = (p != NULL && (uintptr_t)(p) % 32 == 0); mi_free(p); - }); - CHECK_BODY("malloc-aligned2", { + }; + CHECK_BODY("malloc-aligned2") { void* p = mi_malloc_aligned(48,32); result = (p != NULL && (uintptr_t)(p) % 32 == 0); mi_free(p); - }); - CHECK_BODY("malloc-aligned3", { - void* p1 = mi_malloc_aligned(48,32); bool result1 = (p1 != NULL && (uintptr_t)(p1) % 32 == 0); + }; + CHECK_BODY("malloc-aligned3") { + void* p1 = mi_malloc_aligned(48,32); bool result1 = (p1 != NULL && (uintptr_t)(p1) % 32 == 0); void* p2 = mi_malloc_aligned(48,32); bool result2 = (p2 != NULL && (uintptr_t)(p2) % 32 == 0); mi_free(p2); mi_free(p1); result = (result1&&result2); - }); - CHECK_BODY("malloc-aligned4", { + }; + CHECK_BODY("malloc-aligned4") { void* p; bool ok = true; for (int i = 0; i < 8 && ok; i++) { @@ -151,17 +136,89 @@ int main() { ok = (p != NULL && (uintptr_t)(p) % 16 == 0); mi_free(p); } result = ok; - }); - CHECK_BODY("malloc-aligned5", { - void* p = mi_malloc_aligned(4097,4096); size_t usable = mi_usable_size(p); result = usable >= 4097 && usable < 10000; mi_free(p); - }); - CHECK_BODY("malloc-aligned-at1", { + }; + CHECK_BODY("malloc-aligned5") { + void* p = mi_malloc_aligned(4097,4096); + size_t usable = mi_usable_size(p); + result = (usable >= 4097 && usable < 16000); + printf("malloc_aligned5: usable size: %zi\n", usable); + mi_free(p); + }; + CHECK_BODY("malloc-aligned6") { + bool ok = true; + for (size_t align = 1; align <= MI_ALIGNMENT_MAX && ok; align *= 2) { + void* ps[8]; + for (int i = 0; i < 8 && ok; i++) { + ps[i] = mi_malloc_aligned(align*13 // size + , align); + if (ps[i] == NULL || (uintptr_t)(ps[i]) % align != 0) { + ok = false; + } + } + for (int i = 0; i < 8 && ok; i++) { + mi_free(ps[i]); + } + } + result = ok; + }; + CHECK_BODY("malloc-aligned7") { + void* p = mi_malloc_aligned(1024,MI_ALIGNMENT_MAX); + mi_free(p); + result = ((uintptr_t)p % MI_ALIGNMENT_MAX) == 0; + }; + CHECK_BODY("malloc-aligned8") { + bool ok = true; + for (int i = 0; i < 5 && ok; i++) { + int n = (1 << i); + void* p = mi_malloc_aligned(1024, n * MI_ALIGNMENT_MAX); + ok = ((uintptr_t)p % (n*MI_ALIGNMENT_MAX)) == 0; + mi_free(p); + } + result = ok; + }; + CHECK_BODY("malloc-aligned9") { + bool ok = true; + void* p[8]; + size_t sizes[8] = { 8, 512, 1024 * 1024, MI_ALIGNMENT_MAX, MI_ALIGNMENT_MAX + 1, 2 * MI_ALIGNMENT_MAX, 8 * MI_ALIGNMENT_MAX, 0 }; + for (int i = 0; i < 28 && ok; i++) { + int align = (1 << i); + for (int j = 0; j < 8 && ok; j++) { + p[j] = mi_zalloc_aligned(sizes[j], align); + ok = ((uintptr_t)p[j] % align) == 0; + } + for (int j = 0; j < 8; j++) { + mi_free(p[j]); + } + } + result = ok; + }; + CHECK_BODY("malloc-aligned10") { + bool ok = true; + void* p[10+1]; + int align; + int j; + for(j = 0, align = 1; j <= 10 && ok; align *= 2, j++ ) { + p[j] = mi_malloc_aligned(43 + align, align); + ok = ((uintptr_t)p[j] % align) == 0; + } + for ( ; j > 0; j--) { + mi_free(p[j-1]); + } + result = ok; + } + CHECK_BODY("malloc_aligned11") { + mi_heap_t* heap = mi_heap_new(); + void* p = mi_heap_malloc_aligned(heap, 33554426, 8); + result = mi_heap_contains_block(heap, p); + mi_heap_destroy(heap); + } + CHECK_BODY("malloc-aligned-at1") { void* p = mi_malloc_aligned_at(48,32,0); result = (p != NULL && ((uintptr_t)(p) + 0) % 32 == 0); mi_free(p); - }); - CHECK_BODY("malloc-aligned-at2", { + }; + CHECK_BODY("malloc-aligned-at2") { void* p = mi_malloc_aligned_at(50,32,8); result = (p != NULL && ((uintptr_t)(p) + 8) % 32 == 0); mi_free(p); - }); - CHECK_BODY("memalign1", { + }; + CHECK_BODY("memalign1") { void* p; bool ok = true; for (int i = 0; i < 8 && ok; i++) { @@ -169,7 +226,35 @@ int main() { ok = (p != NULL && (uintptr_t)(p) % 16 == 0); mi_free(p); } result = ok; - }); + }; + + // --------------------------------------------------- + // Reallocation + // --------------------------------------------------- + CHECK_BODY("realloc-null") { + void* p = mi_realloc(NULL,4); + result = (p != NULL); + mi_free(p); + }; + + CHECK_BODY("realloc-null-sizezero") { + void* p = mi_realloc(NULL,0); // "If ptr is NULL, the behavior is the same as calling malloc(new_size)." + result = (p != NULL); + mi_free(p); + }; + + CHECK_BODY("realloc-sizezero") { + void* p = mi_malloc(4); + void* q = mi_realloc(p, 0); + result = (q != NULL); + mi_free(q); + }; + + CHECK_BODY("reallocarray-null-sizezero") { + void* p = mi_reallocarray(NULL,0,16); // issue #574 + result = (p != NULL && errno == 0); + mi_free(p); + }; // --------------------------------------------------- // Heaps @@ -182,11 +267,11 @@ int main() { // --------------------------------------------------- // various // --------------------------------------------------- - CHECK_BODY("realpath", { + CHECK_BODY("realpath") { char* s = mi_realpath( ".", NULL ); // printf("realpath: %s\n",s); mi_free(s); - }); + }; CHECK("stl_allocator1", test_stl_allocator1()); CHECK("stl_allocator2", test_stl_allocator2()); @@ -194,10 +279,7 @@ int main() { // --------------------------------------------------- // Done // ---------------------------------------------------[] - fprintf(stderr,"\n\n---------------------------------------------\n" - "succeeded: %i\n" - "failed : %i\n\n", ok, failed); - return failed; + return print_test_summary(); } // --------------------------------------------------- diff --git a/Source/mimalloc/test/test-stress.c b/Source/mimalloc/test/test-stress.c index d45e9899b..f4cb78b4e 100644 --- a/Source/mimalloc/test/test-stress.c +++ b/Source/mimalloc/test/test-stress.c @@ -7,7 +7,7 @@ terms of the MIT license. /* This is a stress test for the allocator, using multiple threads and transferring objects between threads. It tries to reflect real-world workloads: - allocation size is distributed linearly in powers of two - - with some fraction extra large (and some extra extra large) + - with some fraction extra large (and some very large) - the allocations are initialized and read again at free - pointers transfer between threads - threads are terminated and recreated with some objects surviving in between @@ -25,7 +25,7 @@ terms of the MIT license. // // argument defaults static int THREADS = 32; // more repeatable if THREADS <= #processors -static int SCALE = 10; // scaling factor +static int SCALE = 25; // scaling factor static int ITER = 50; // N full iterations destructing and re-creating all threads // static int THREADS = 8; // more repeatable if THREADS <= #processors @@ -39,12 +39,12 @@ static size_t use_one_size = 0; // use single object size of `N * s // #define USE_STD_MALLOC #ifdef USE_STD_MALLOC -#define custom_calloc(n,s) calloc(n,s) +#define custom_calloc(n,s) malloc(n*s) #define custom_realloc(p,s) realloc(p,s) #define custom_free(p) free(p) #else #include -#define custom_calloc(n,s) mi_calloc(n,s) +#define custom_calloc(n,s) mi_malloc(n*s) #define custom_realloc(p,s) mi_realloc(p,s) #define custom_free(p) mi_free(p) #endif @@ -182,17 +182,20 @@ static void run_os_threads(size_t nthreads, void (*entry)(intptr_t tid)); static void test_stress(void) { uintptr_t r = rand(); for (int n = 0; n < ITER; n++) { - run_os_threads(THREADS, &stress); + run_os_threads(THREADS, &stress); for (int i = 0; i < TRANSFERS; i++) { if (chance(50, &r) || n + 1 == ITER) { // free all on last run, otherwise free half of the transfers void* p = atomic_exchange_ptr(&transfer[i], NULL); free_items(p); } } - // mi_collect(false); -#if !defined(NDEBUG) || defined(MI_TSAN) + #ifndef NDEBUG + //mi_collect(false); + //mi_debug_show_arenas(); + #endif + #if !defined(NDEBUG) || defined(MI_TSAN) if ((n + 1) % 10 == 0) { printf("- iterations left: %3d\n", ITER - (n + 1)); } -#endif + #endif } } @@ -218,7 +221,7 @@ static void test_leak(void) { } #endif -int main(int argc, char** argv) { +int main(int argc, char** argv) { // > mimalloc-test-stress [THREADS] [SCALE] [ITER] if (argc >= 2) { char* end; @@ -241,19 +244,29 @@ int main(int argc, char** argv) { //printf("(reserve huge: %i\n)", res); //bench_start_program(); +#ifndef USE_STD_MALLOC + mi_stats_reset(); +#endif // Run ITER full iterations where half the objects in the transfer buffer survive to the next round. srand(0x7feb352d); - // mi_stats_reset(); + + //mi_reserve_os_memory(512ULL << 20, true, true); + +#if !defined(NDEBUG) && !defined(USE_STD_MALLOC) + mi_stats_reset(); +#endif + #ifdef STRESS - test_stress(); + test_stress(); #else - test_leak(); + test_leak(); #endif #ifndef USE_STD_MALLOC #ifndef NDEBUG mi_collect(true); + //mi_debug_show_arenas(); #endif mi_stats_print(NULL); #endif diff --git a/Source/mimalloc/test/test-wrong.c b/Source/mimalloc/test/test-wrong.c new file mode 100644 index 000000000..56a2339a7 --- /dev/null +++ b/Source/mimalloc/test/test-wrong.c @@ -0,0 +1,92 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2020, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +/* test file for valgrind/asan support. + + VALGRIND: + ---------- + Compile in an "out/debug" folder: + + > cd out/debug + > cmake ../.. -DMI_TRACK_VALGRIND=1 + > make -j8 + + and then compile this file as: + + > gcc -g -o test-wrong -I../../include ../../test/test-wrong.c libmimalloc-valgrind-debug.a -lpthread + + and test as: + + > valgrind ./test-wrong + + + ASAN + ---------- + Compile in an "out/debug" folder: + + > cd out/debug + > cmake ../.. -DMI_TRACK_ASAN=1 + > make -j8 + + and then compile this file as: + + > clang -g -o test-wrong -I../../include ../../test/test-wrong.c libmimalloc-asan-debug.a -lpthread -fsanitize=address -fsanitize-recover=address + + and test as: + + > ASAN_OPTIONS=verbosity=1:halt_on_error=0 ./test-wrong + + +*/ +#include +#include +#include "mimalloc.h" + +#ifdef USE_STD_MALLOC +# define mi(x) x +#else +# define mi(x) mi_##x +#endif + +int main(int argc, char** argv) { + int* p = (int*)mi(malloc)(3*sizeof(int)); + + int* r = (int*)mi_malloc_aligned(8,16); + mi_free(r); + + // illegal byte wise read + char* c = (char*)mi(malloc)(3); + printf("invalid byte: over: %d, under: %d\n", c[4], c[-1]); + mi(free)(c); + + // undefined access + int* q = (int*)mi(malloc)(sizeof(int)); + printf("undefined: %d\n", *q); + + // illegal int read + printf("invalid: over: %d, under: %d\n", q[1], q[-1]); + + *q = 42; + + // buffer overflow + q[1] = 43; + + // buffer underflow + q[-1] = 44; + + mi(free)(q); + + // double free + mi(free)(q); + + // use after free + printf("use-after-free: %d\n", *q); + + // leak p + // mi_free(p) + return 0; +} \ No newline at end of file diff --git a/Source/mimalloc/test/testhelper.h b/Source/mimalloc/test/testhelper.h new file mode 100644 index 000000000..a97275841 --- /dev/null +++ b/Source/mimalloc/test/testhelper.h @@ -0,0 +1,49 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2020, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ +#ifndef TESTHELPER_H_ +#define TESTHELPER_H_ + +#include +#include +#include + +// --------------------------------------------------------------------------- +// Test macros: CHECK(name,predicate) and CHECK_BODY(name,body) +// --------------------------------------------------------------------------- +static int ok = 0; +static int failed = 0; + +static bool check_result(bool result, const char* testname, const char* fname, long lineno) { + if (!(result)) { + failed++; + fprintf(stderr,"\n FAILED: %s: %s:%ld\n", testname, fname, lineno); + /* exit(1); */ + } + else { + ok++; + fprintf(stderr, "ok.\n"); + } + return true; +} + +#define CHECK_BODY(name) \ + fprintf(stderr,"test: %s... ", name ); \ + errno = 0; \ + for(bool done = false, result = true; !done; done = check_result(result,name,__FILE__,__LINE__)) + +#define CHECK(name,expr) CHECK_BODY(name){ result = (expr); } + +// Print summary of test. Return value can be directly use as a return value for main(). +static inline int print_test_summary(void) +{ + fprintf(stderr,"\n\n---------------------------------------------\n" + "succeeded: %i\n" + "failed : %i\n\n", ok, failed); + return failed; +} + +#endif // TESTHELPER_H_ From bd6810e2025334b515870012c4aab17e10a78108 Mon Sep 17 00:00:00 2001 From: Adam Simmons Date: Fri, 21 Apr 2023 12:53:41 -0500 Subject: [PATCH 06/13] Integrate new mimalloc version into build system. --- Source/mimalloc/CMakeLists.txt | 43 ++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/Source/mimalloc/CMakeLists.txt b/Source/mimalloc/CMakeLists.txt index 35d5d6509..72ddc7235 100644 --- a/Source/mimalloc/CMakeLists.txt +++ b/Source/mimalloc/CMakeLists.txt @@ -1,13 +1,10 @@ -cmake_minimum_required(VERSION 3.13) -project(libmimalloc C CXX) - set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) option(MI_SECURE "Use full security mitigations (like guard pages, allocation randomization, double-free mitigation, and free-list corruption detection)" OFF) option(MI_DEBUG_FULL "Use full internal heap invariant checking in DEBUG mode (expensive)" OFF) option(MI_PADDING "Enable padding to detect heap block overflow (always on in DEBUG or SECURE mode, or with Valgrind/ASAN)" OFF) -option(MI_OVERRIDE "Override the standard malloc interface (e.g. define entry points for malloc() etc)" ON) +option(MI_OVERRIDE "Override the standard malloc interface (e.g. define entry points for malloc() etc)" OFF) option(MI_XMALLOC "Enable abort() call on memory allocation failure by default" OFF) option(MI_SHOW_ERRORS "Show error and warning messages by default (only enabled by default in DEBUG mode)" OFF) option(MI_TRACK_VALGRIND "Compile with Valgrind support (adds a small overhead)" OFF) @@ -15,14 +12,14 @@ option(MI_TRACK_ASAN "Compile with address sanitizer support (adds a smal option(MI_TRACK_ETW "Compile with Windows event tracing (ETW) support (adds a small overhead)" OFF) option(MI_USE_CXX "Use the C++ compiler to compile the library (instead of the C compiler)" OFF) option(MI_SEE_ASM "Generate assembly files" OFF) -option(MI_OSX_INTERPOSE "Use interpose to override standard malloc on macOS" ON) -option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" ON) -option(MI_WIN_REDIRECT "Use redirection module ('mimalloc-redirect') on Windows if compiling mimalloc as a DLL" ON) -option(MI_LOCAL_DYNAMIC_TLS "Use slightly slower, dlopen-compatible TLS mechanism (Unix)" OFF) -option(MI_BUILD_SHARED "Build shared library" ON) -option(MI_BUILD_STATIC "Build static library" ON) -option(MI_BUILD_OBJECT "Build object library" ON) -option(MI_BUILD_TESTS "Build test executables" ON) +option(MI_OSX_INTERPOSE "Use interpose to override standard malloc on macOS" OFF) +option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" OFF) +option(MI_WIN_REDIRECT "Use redirection module ('mimalloc-redirect') on Windows if compiling mimalloc as a DLL" OFF) +option(MI_LOCAL_DYNAMIC_TLS "Use slightly slower, dlopen-compatible TLS mechanism (Unix)" ON) +option(MI_BUILD_SHARED "Build shared library" OFF) +option(MI_BUILD_STATIC "Build static library" OFF) +option(MI_BUILD_OBJECT "Build object library" OFF) +option(MI_BUILD_TESTS "Build test executables" OFF) option(MI_DEBUG_TSAN "Build with thread sanitizer (needs clang)" OFF) option(MI_DEBUG_UBSAN "Build with undefined-behavior sanitizer (needs clang++)" OFF) option(MI_SKIP_COLLECT_ON_EXIT "Skip collecting memory on program exit" OFF) @@ -524,3 +521,25 @@ if (MI_OVERRIDE) endif() endif() endif() + + +set(mimalloc_SOURCES ${mi_sources}) +set(mimalloc_LIBRARIES ${mi_libraries}) +set(mimalloc_INTERFACE_LIBRARIES mimalloc) +set(mimalloc_INTERFACE_INCLUDE_DIRECTORIES ${mimalloc_FRAMEWORK_HEADERS_DIR}) +set(mimalloc_INTERFACE_DEPENDENCIES mimalloc_CopyHeaders) +set(mimalloc_PRIVATE_DEFINITIONS ${mi_defines} MI_STATIC_LIB) +set(mimalloc_COMPILE_OPTIONS ${mi_cflags}) +set(mimalloc_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include) + +WEBKIT_FRAMEWORK_DECLARE(mimalloc) +WEBKIT_INCLUDE_CONFIG_FILES_IF_EXISTS() + +WEBKIT_COPY_FILES(mimalloc_CopyHeaders + DESTINATION ${mimalloc_FRAMEWORK_HEADERS_DIR} + FILES ${mimalloc_PUBLIC_HEADERS} +) + +WEBKIT_WRAP_SOURCELIST(${mimalloc_SOURCES}) +WEBKIT_FRAMEWORK(mimalloc) +WEBKIT_FRAMEWORK_TARGET(mimalloc) \ No newline at end of file From 771a5545ba08832333c9f6a8a87a5e74420fcf49 Mon Sep 17 00:00:00 2001 From: Adam Simmons Date: Fri, 21 Apr 2023 16:25:50 -0500 Subject: [PATCH 07/13] Annotate more functions for performance profiling. --- Source/WebCore/loader/SubresourceLoader.cpp | 1 + Source/WebCore/platform/graphics/GraphicsContext.cpp | 5 +++++ Source/WebCore/platform/graphics/ImageSource.cpp | 2 ++ .../WebCore/platform/image-decoders/ScalableImageDecoder.cpp | 1 + .../WebCore/platform/image-decoders/png/PNGImageDecoder.cpp | 2 ++ .../platform/network/curl/CurlResourceHandleDelegate.cpp | 3 +++ 6 files changed, 14 insertions(+) diff --git a/Source/WebCore/loader/SubresourceLoader.cpp b/Source/WebCore/loader/SubresourceLoader.cpp index 41331a011..3a3ed4309 100644 --- a/Source/WebCore/loader/SubresourceLoader.cpp +++ b/Source/WebCore/loader/SubresourceLoader.cpp @@ -463,6 +463,7 @@ void SubresourceLoader::didReceiveBuffer(Ref&& buffer, long long e void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, RefPtr&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType) { + ProfiledZone; ASSERT(m_resource); if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors()) diff --git a/Source/WebCore/platform/graphics/GraphicsContext.cpp b/Source/WebCore/platform/graphics/GraphicsContext.cpp index 46ca46f39..a001dc0f0 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext.cpp +++ b/Source/WebCore/platform/graphics/GraphicsContext.cpp @@ -655,6 +655,7 @@ float GraphicsContext::drawText(const FontCascade& font, const TextRun& run, con void GraphicsContext::drawGlyphs(const Font& font, const GlyphBuffer& buffer, unsigned from, unsigned numGlyphs, const FloatPoint& point, FontSmoothingMode fontSmoothingMode) { + ProfiledZone; if (paintingDisabled()) return; @@ -726,6 +727,7 @@ ImageDrawResult GraphicsContext::drawImage(Image& image, const FloatRect& destin ImageDrawResult GraphicsContext::drawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) { + ProfiledZone; if (paintingDisabled()) return ImageDrawResult::DidNothing; @@ -738,6 +740,7 @@ ImageDrawResult GraphicsContext::drawImage(Image& image, const FloatRect& destin ImageDrawResult GraphicsContext::drawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions) { + ProfiledZone; if (paintingDisabled()) return ImageDrawResult::DidNothing; @@ -751,6 +754,7 @@ ImageDrawResult GraphicsContext::drawTiledImage(Image& image, const FloatRect& d ImageDrawResult GraphicsContext::drawTiledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions) { + ProfiledZone; if (paintingDisabled()) return ImageDrawResult::DidNothing; @@ -778,6 +782,7 @@ void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatRect& desti void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) { + ProfiledZone; if (paintingDisabled()) return; diff --git a/Source/WebCore/platform/graphics/ImageSource.cpp b/Source/WebCore/platform/graphics/ImageSource.cpp index a872676f7..35971a3a9 100644 --- a/Source/WebCore/platform/graphics/ImageSource.cpp +++ b/Source/WebCore/platform/graphics/ImageSource.cpp @@ -96,6 +96,7 @@ bool ImageSource::ensureDecoderAvailable(SharedBuffer* data) void ImageSource::setData(SharedBuffer* data, bool allDataReceived) { + ProfiledZone; if (!data || !ensureDecoderAvailable(data)) return; @@ -104,6 +105,7 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) void ImageSource::resetData(SharedBuffer* data) { + ProfiledZone; m_decoder = nullptr; setData(data, isAllDataReceived()); } diff --git a/Source/WebCore/platform/image-decoders/ScalableImageDecoder.cpp b/Source/WebCore/platform/image-decoders/ScalableImageDecoder.cpp index 3ab79b166..7672a3802 100644 --- a/Source/WebCore/platform/image-decoders/ScalableImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/ScalableImageDecoder.cpp @@ -250,6 +250,7 @@ Seconds ScalableImageDecoder::frameDurationAtIndex(size_t index) const NativeImagePtr ScalableImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel, const DecodingOptions&) { + ProfiledZone; LockHolder lockHolder(m_mutex); // Zero-height images can cause problems for some ports. If we have an empty image dimension, just bail. if (size().isEmpty()) diff --git a/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp index f17bd3e0d..0fe6dbb45 100644 --- a/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp @@ -289,6 +289,7 @@ bool PNGImageDecoder::setFailed() void PNGImageDecoder::headerAvailable() { + ProfiledZone; png_structp png = m_reader->pngPtr(); png_infop info = m_reader->infoPtr(); png_uint_32 width = png_get_image_width(png, info); @@ -552,6 +553,7 @@ void PNGImageDecoder::pngComplete() void PNGImageDecoder::decode(bool onlySize, unsigned haltAtFrame, bool allDataReceived) { + ProfiledZone; if (failed()) return; diff --git a/Source/WebCore/platform/network/curl/CurlResourceHandleDelegate.cpp b/Source/WebCore/platform/network/curl/CurlResourceHandleDelegate.cpp index 06e5124ec..4deef04ea 100644 --- a/Source/WebCore/platform/network/curl/CurlResourceHandleDelegate.cpp +++ b/Source/WebCore/platform/network/curl/CurlResourceHandleDelegate.cpp @@ -102,6 +102,7 @@ static void handleCookieHeaders(ResourceHandleInternal* d, const ResourceRequest void CurlResourceHandleDelegate::curlDidReceiveResponse(CurlRequest& request, CurlResponse&& receivedResponse) { + ProfiledZone; ProfiledMemoryZone(MemoryTag::Network); ASSERT(isMainThread()); ASSERT(!d()->m_defersLoading); @@ -148,6 +149,7 @@ void CurlResourceHandleDelegate::curlDidReceiveResponse(CurlRequest& request, Cu void CurlResourceHandleDelegate::curlDidComplete(CurlRequest&, NetworkLoadMetrics&&) { + ProfiledZone; ProfiledMemoryZone(MemoryTag::Network); ASSERT(isMainThread()); @@ -172,6 +174,7 @@ void CurlResourceHandleDelegate::curlDidFailWithError(CurlRequest&, ResourceErro void CurlResourceHandleDelegate::curlConsumeReceiveQueue(CurlRequest&, WTF::ReaderWriterQueue>& queue) { + ProfiledZone; ProfiledMemoryZone(MemoryTag::Network); ASSERT(isMainThread()); From 30c54e724ae84e2f6e6a22e44d426b3f5afcd1a6 Mon Sep 17 00:00:00 2001 From: Adam Simmons Date: Thu, 29 Jun 2023 15:58:05 -0500 Subject: [PATCH 08/13] Remove limit on gradient stops (was capped at 12). --- .../platform/graphics/GradientImage.cpp | 11 ++++++++-- .../ultralight/GradientUltralight.cpp | 14 ++++++------- .../ultralight/RenderThemeUltralight.cpp | 21 ++++++++++++------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/Source/WebCore/platform/graphics/GradientImage.cpp b/Source/WebCore/platform/graphics/GradientImage.cpp index 8674dc3b0..c6c538c8d 100644 --- a/Source/WebCore/platform/graphics/GradientImage.cpp +++ b/Source/WebCore/platform/graphics/GradientImage.cpp @@ -28,6 +28,9 @@ #include "GraphicsContext.h" #include "ImageBuffer.h" +#if USE(ULTRALIGHT) +#include "PlatformContextUltralight.h" +#endif namespace WebCore { @@ -65,8 +68,12 @@ void GradientImage::drawPattern(GraphicsContext& destContext, const FloatRect& d double xScale = fabs(destContextCTM.xScale()); double yScale = fabs(destContextCTM.yScale()); #if USE(ULTRALIGHT) - xScale = 1.0; - yScale = 1.0; + // FIXME: Disable upscaling when GPU-backed Canvas is being used due to shader transform math issues + auto platformContext = destContext.platformContext(); + if (!platformContext->canvas()->surface()) { + xScale = 1.0; + yScale = 1.0; + } #endif AffineTransform adjustedPatternCTM = patternTransform; adjustedPatternCTM.scale(1.0 / xScale, 1.0 / yScale); diff --git a/Source/WebCore/platform/graphics/ultralight/GradientUltralight.cpp b/Source/WebCore/platform/graphics/ultralight/GradientUltralight.cpp index 8928a6b12..f8b5de345 100644 --- a/Source/WebCore/platform/graphics/ultralight/GradientUltralight.cpp +++ b/Source/WebCore/platform/graphics/ultralight/GradientUltralight.cpp @@ -25,7 +25,6 @@ namespace WebCore { [&](const LinearData& data) -> PlatformGradient { auto grad = new ultralight::Gradient(); grad->is_radial = false; - grad->num_stops = 0; grad->p0 = ultralight::Point(data.point0.x(), data.point0.y()); grad->p1 = ultralight::Point(data.point1.x(), data.point1.y()); return grad; @@ -33,7 +32,6 @@ namespace WebCore { [&](const RadialData& data) -> PlatformGradient { auto grad = new ultralight::Gradient(); grad->is_radial = true; - grad->num_stops = 0; grad->p0 = ultralight::Point(data.point0.x(), data.point0.y()); grad->p1 = ultralight::Point(data.point1.x(), data.point1.y()); grad->r0 = data.startRadius; @@ -58,14 +56,14 @@ namespace WebCore { size_t num_stops = m_stops.size(); - // Clamp to 12 stops - if (num_stops > 12) - num_stops = 12; + // Reserve capacity ahead of time to reduce reallocations + m_gradient->stops.reserve(num_stops); - m_gradient->num_stops = num_stops; for (size_t i = 0; i < num_stops; ++i) { - m_gradient->stops[i].stop = m_stops[i].offset; - m_gradient->stops[i].color = UltralightRGBA(m_stops[i].color.red(), m_stops[i].color.green(), m_stops[i].color.blue(), m_stops[i].color.alpha()); + ultralight::GradientStop stop; + stop.stop = m_stops[i].offset; + stop.color = UltralightRGBA(m_stops[i].color.red(), m_stops[i].color.green(), m_stops[i].color.blue(), m_stops[i].color.alpha()); + m_gradient->stops.push_back(stop); } return m_gradient; diff --git a/Source/WebCore/platform/ultralight/RenderThemeUltralight.cpp b/Source/WebCore/platform/ultralight/RenderThemeUltralight.cpp index c456e3d9b..f8bf1a772 100644 --- a/Source/WebCore/platform/ultralight/RenderThemeUltralight.cpp +++ b/Source/WebCore/platform/ultralight/RenderThemeUltralight.cpp @@ -228,21 +228,26 @@ class RenderThemeUltralight : public RenderTheme { ultralight::Gradient gradient; if (simulateExponential) { constexpr size_t num_samples = 7; - gradient.num_stops = num_samples; + gradient.stops.reserve(num_samples); ultralight::vec4 colorA = UltralightColorGetFloat4(colorPair->color1); ultralight::vec4 colorB = UltralightColorGetFloat4(colorPair->color2); for (size_t i = 0; i < num_samples; ++i) { float t = i / (num_samples - 1.0f); - gradient.stops[i].color = sampleExponential(colorA, colorB, t); - gradient.stops[i].stop = t; + ultralight::GradientStop s; + s.color = sampleExponential(colorA, colorB, t); + s.stop = t; + gradient.stops.push_back(s); } } else { - gradient.num_stops = 2; - gradient.stops[0].color = colorPair->color1; - gradient.stops[0].stop = 0.0f; - gradient.stops[1].color = colorPair->color2; - gradient.stops[1].stop = 1.0f; + gradient.stops.reserve(2); + ultralight::GradientStop s; + s.color = colorPair->color1; + s.stop = 0.0f; + gradient.stops.push_back(s); + s.color = colorPair->color2; + s.stop = 1.0f; + gradient.stops.push_back(s); } return gradient; } From cd46edc61c59d28a18657f2dc048cba82a7c7780 Mon Sep 17 00:00:00 2001 From: Adam Simmons Date: Wed, 5 Jul 2023 19:11:18 -0500 Subject: [PATCH 09/13] Update CA certificate bundle (latest Mozilla). --- cacert.pem | 995 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 602 insertions(+), 393 deletions(-) diff --git a/cacert.pem b/cacert.pem index a1dc575dd..6b93dc34f 100644 --- a/cacert.pem +++ b/cacert.pem @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Thu Sep 30 03:12:05 2021 GMT +## Certificate data from Mozilla as of: Tue May 30 03:12:04 2023 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -13,8 +13,8 @@ ## an Apache+mod_ssl webserver for SSL client authentication. ## Just configure this file as the SSLCACertificateFile. ## -## Conversion done with mk-ca-bundle.pl version 1.28. -## SHA256: c8f6733d1ff4e6a4769c182971a1234f95ae079247a9c439a13423fe8ba5c24f +## Conversion done with mk-ca-bundle.pl version 1.29. +## SHA256: c47475103fb05bb562bbadff0d1e72346b03236154e1448a6ca191b740f83507 ## @@ -39,28 +39,6 @@ hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== -----END CERTIFICATE----- -GlobalSign Root CA - R2 -======================= ------BEGIN CERTIFICATE----- -MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv -YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh -bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT -aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln -bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 -ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp -s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN -S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL -TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C -ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E -FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i -YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN -BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp -9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu -01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 -9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 -TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== ------END CERTIFICATE----- - Entrust.net Premium 2048 Secure Server CA ========================================= -----BEGIN CERTIFICATE----- @@ -511,29 +489,6 @@ IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN +8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== -----END CERTIFICATE----- -Network Solutions Certificate Authority -======================================= ------BEGIN CERTIFICATE----- -MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG -EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr -IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx -MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu -MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx -jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT -aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT -crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc -/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB -AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv -bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA -A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q -4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ -GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv -wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD -ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey ------END CERTIFICATE----- - COMODO ECC Certification Authority ================================== -----BEGIN CERTIFICATE----- @@ -573,28 +528,6 @@ PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== -----END CERTIFICATE----- -Cybertrust Global Root -====================== ------BEGIN CERTIFICATE----- -MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li -ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 -MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD -ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -+Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW -0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL -AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin -89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT -8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 -MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G -A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO -lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi -5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 -hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T -X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW -WL1WMRJOEcgh4LMRkWXbtKaIOM5V ------END CERTIFICATE----- - ePKI Root Certification Authority ================================= -----BEGIN CERTIFICATE----- @@ -670,26 +603,6 @@ NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= -----END CERTIFICATE----- -Hongkong Post Root CA 1 -======================= ------BEGIN CERTIFICATE----- -MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT -DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx -NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n -IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 -ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr -auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh -qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY -V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV -HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i -h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio -l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei -IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps -T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT -c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== ------END CERTIFICATE----- - SecureSign RootCA11 =================== -----BEGIN CERTIFICATE----- @@ -1037,60 +950,6 @@ tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 -----END CERTIFICATE----- -EC-ACC -====== ------BEGIN CERTIFICATE----- -MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE -BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w -ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD -VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE -CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT -BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 -MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt -SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl -Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh -cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK -w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT -ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 -HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a -E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw -0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD -VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 -Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l -dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ -lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa -Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe -l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 -E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D -5EI= ------END CERTIFICATE----- - -Hellenic Academic and Research Institutions RootCA 2011 -======================================================= ------BEGIN CERTIFICATE----- -MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT -O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y -aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z -IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT -AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z -IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo -IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI -1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa -71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u -8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH -3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ -MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 -MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu -b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt -XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 -TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD -/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N -7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 ------END CERTIFICATE----- - Actalis Authentication Root CA ============================== -----BEGIN CERTIFICATE----- @@ -1382,40 +1241,6 @@ Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= -----END CERTIFICATE----- -E-Tugra Certification Authority -=============================== ------BEGIN CERTIFICATE----- -MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w -DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls -ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN -ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw -NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx -QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl -cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD -DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd -hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K -CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g -ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ -BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 -E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz -rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq -jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn -rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 -dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB -/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG -MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK -kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO -XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 -VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo -a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc -dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV -KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT -Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 -8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G -C7TbO6Orb1wdtn7os4I07QZcJA== ------END CERTIFICATE----- - T-TeleSec GlobalRoot Class 2 ============================ -----BEGIN CERTIFICATE----- @@ -1737,20 +1562,6 @@ HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu 9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= -----END CERTIFICATE----- -GlobalSign ECC Root CA - R4 -=========================== ------BEGIN CERTIFICATE----- -MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb -R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD -EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb -R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD -EwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl -OQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P -AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV -MAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF -JzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q= ------END CERTIFICATE----- - GlobalSign ECC Root CA - R5 =========================== -----BEGIN CERTIFICATE----- @@ -1766,36 +1577,6 @@ uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 -----END CERTIFICATE----- -Staat der Nederlanden EV Root CA -================================ ------BEGIN CERTIFICATE----- -MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE -CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g -RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M -MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl -cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk -SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW -O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r -0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8 -Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV -XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr -08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV -0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd -74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx -fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa -ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI -eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu -c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq -5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN -b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN -f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi -5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4 -WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK -DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy -eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg== ------END CERTIFICATE----- - IdenTrust Commercial Root CA 1 ============================== -----BEGIN CERTIFICATE----- @@ -2247,87 +2028,6 @@ F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== -----END CERTIFICATE----- -TrustCor RootCert CA-1 -====================== ------BEGIN CERTIFICATE----- -MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP -MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig -U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp -dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx -MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu -YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe -VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy -dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq -jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4 -pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0 -JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h -gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw -/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j -BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5 -mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf -ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C -qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P -3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk= ------END CERTIFICATE----- - -TrustCor RootCert CA-2 -====================== ------BEGIN CERTIFICATE----- -MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w -DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT -eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0 -eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy -MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h -bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U -cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0 -IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb -ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk -RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1 -oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb -XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1 -/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q -jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP -eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg -rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh -8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU -2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD -VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h -Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp -kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv -2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3 -S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw -PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv -DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU -RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE -xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX -RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ ------END CERTIFICATE----- - -TrustCor ECA-1 -============== ------BEGIN CERTIFICATE----- -MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP -MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig -U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp -dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw -N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5 -MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y -IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR -MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23 -xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc -p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+ -fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj -YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL -f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF -AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u -/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F -hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs -J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC -jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g== ------END CERTIFICATE----- - SSL.com Root Certification Authority RSA ======================================== -----BEGIN CERTIFICATE----- @@ -2472,96 +2172,6 @@ AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 -----END CERTIFICATE----- -GTS Root R1 -=========== ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG -EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv -b3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG -A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx -9vaMf/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7r -aKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnW -r4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqM -LnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly -4cpk9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr -06zqkUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 -wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om -3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNu -JLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEM -BQADggIBADiWCu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 -d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6ZXPYfcX3v73sv -fuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZRgyFmxhE+885H7pwoHyXa/6xm -ld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9b -gsiG1eGZbYwE8na6SfZu6W0eX6DvJ4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq -4BjFbkerQUIpm/ZgDdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWEr -tXvM+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyyF62ARPBo -pY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9SQ98POyDGCBDTtWTurQ0 -sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdwsE3PYJ/HQcu51OyLemGhmW/HGY0dVHLql -CFF1pkgl ------END CERTIFICATE----- - -GTS Root R2 -=========== ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG -EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv -b3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAG -A1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTuk -k3LvCvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo -7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWI -m8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5Gm -dFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbu -ak7MkogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscsz -cTJGr61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW -Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73Vululycsl -aVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy -5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEM -BQADggIBALZp8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT -vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiTz9D2PGcDFWEJ -+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiApJiS4wGWAqoC7o87xdFtCjMw -c3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvbpxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3Da -WsYDQvTtN6LwG1BUSw7YhN4ZKJmBR64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5r -n/WkhLx3+WuXrD5RRaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56Gtmwfu -Nmsk0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC5AwiWVIQ -7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiFizoHCBy69Y9Vmhh1fuXs -gWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLnyOd/xCxgXS/Dr55FBcOEArf9LAhST4Ld -o/DUhgkC ------END CERTIFICATE----- - -GTS Root R3 -=========== ------BEGIN CERTIFICATE----- -MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV -UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg -UjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE -ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUU -Rout736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24Cej -QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP -0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFukfCPAlaUs3L6JbyO5o91lAFJekazInXJ0 -glMLfalAvWhgxeG4VDvBNhcl2MG9AjEAnjWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOa -KaqW04MjyaR7YbPMAuhd ------END CERTIFICATE----- - -GTS Root R4 -=========== ------BEGIN CERTIFICATE----- -MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV -UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg -UjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE -ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa -6zzuhXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqj -QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV -2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0CMRw3J5QdCHojXohw0+WbhXRIjVhLfoI -N+4Zba3bssx9BzT1YBkstTTZbyACMANxsbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11x -zPKwTdb+mciUqXWi4w== ------END CERTIFICATE----- - UCA Global G2 Root ================== -----BEGIN CERTIFICATE----- @@ -3152,3 +2762,602 @@ WWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7DP78v3DSk+yshzWePS/Tj OPQD8rv7gmsHINFSH5pkAnuYZttcTVoP0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZck bxJF0WddCajJFdr60qZfE2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb -----END CERTIFICATE----- + +TunTrust Root CA +================ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +HARICA TLS RSA Root CA 2021 +=========================== +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG +EwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0EgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUz +OFoXDTQ1MDIxMzEwNTUzN1owbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRl +bWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNB +IFJvb3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569lmwVnlskN +JLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE4VGC/6zStGndLuwRo0Xu +a2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uva9of08WRiFukiZLRgeaMOVig1mlDqa2Y +Ulhu2wr7a89o+uOkXjpFc5gH6l8Cct4MpbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K +5FrZx40d/JiZ+yykgmvwKh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEv +dmn8kN3bLW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcYAuUR +0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqBAGMUuTNe3QvboEUH +GjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYqE613TBoYm5EPWNgGVMWX+Ko/IIqm +haZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHrW2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQ +CPxrvrNQKlr9qEgYRtaQQJKQCoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAUX15QvWiWkKQU +EapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3f5Z2EMVGpdAgS1D0NTsY9FVq +QRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxajaH6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxD +QpSbIPDRzbLrLFPCU3hKTwSUQZqPJzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcR +j88YxeMn/ibvBZ3PzzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5 +vZStjBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0/L5H9MG0 +qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pTBGIBnfHAT+7hOtSLIBD6 +Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79aPib8qXPMThcFarmlwDB31qlpzmq6YR/ +PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YWxw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnn +kf3/W9b3raYvAwtt41dU63ZTGI0RmLo= +-----END CERTIFICATE----- + +HARICA TLS ECC Root CA 2021 +=========================== +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQswCQYDVQQGEwJH +UjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBD +QTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoX +DTQ1MDIxMzExMDEwOVowbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWlj +IGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJv +b3QgQ0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7KKrxcm1l +AEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9YSTHMmE5gEYd103KUkE+b +ECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW +0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAi +rcJRQO9gcS3ujwLEXQNwSaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/Qw +CZ61IygNnxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1Ud +DgQWBBRlzeurNR4APn7VdMActHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4w +gZswgZgGBFUdIAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j +b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABCAG8AbgBhAG4A +bwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAwADEANzAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9miWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL +4QjbEwj4KKE1soCzC1HA01aajTNFSa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDb +LIpgD7dvlAceHabJhfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1il +I45PVf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZEEAEeiGaP +cjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV1aUsIC+nmCjuRfzxuIgA +LI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2tCsvMo2ebKHTEm9caPARYpoKdrcd7b/+A +lun4jWq9GJAd/0kakFI3ky88Al2CdgtR5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH +9IBk9W6VULgRfhVwOEqwf9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpf +NIbnYrX9ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNKGbqE +ZycPvEJdvSRUDewdcAZfpLz6IHxV +-----END CERTIFICATE----- + +vTrus ECC Root CA +================= +-----BEGIN CERTIFICATE----- +MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMwRzELMAkGA1UE +BhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBS +b290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDczMTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAa +BgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+c +ToL0v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUde4BdS49n +TPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIwV53dVvHH4+m4SVBrm2nDb+zDfSXkV5UT +QJtS0zvzQBm8JsctBp61ezaf9SXUY2sAAjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQL +YgmRWAD5Tfs0aNoJrSEGGJTO +-----END CERTIFICATE----- + +vTrus Root CA +============= +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQELBQAwQzELMAkG +A1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xFjAUBgNVBAMTDXZUcnVzIFJv +b3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMxMDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoG +A1UEChMTaVRydXNDaGluYSBDby4sTHRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZots +SKYcIrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykUAyyNJJrI +ZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+GrPSbcKvdmaVayqwlHeF +XgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z98Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KA +YPxMvDVTAWqXcoKv8R1w6Jz1717CbMdHflqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70 +kLJrxLT5ZOrpGgrIDajtJ8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2 +AXPKBlim0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZNpGvu +/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQUqqzApVg+QxMaPnu +1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHWOXSuTEGC2/KmSNGzm/MzqvOmwMVO +9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMBAAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYg +scasGrz2iTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOC +AgEAKbqSSaet8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd +nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1jbhd47F18iMjr +jld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvMKar5CKXiNxTKsbhm7xqC5PD4 +8acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIivTDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJn +xDHO2zTlJQNgJXtxmOTAGytfdELSS8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554Wg +icEFOwE30z9J4nfrI8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4 +sEb9b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNBUvupLnKW +nyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1PTi07NEPhmg4NpGaXutIc +SkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929vensBxXVsFy6K2ir40zSbofitzmdHxghm+H +l3s= +-----END CERTIFICATE----- + +ISRG Root X2 +============ +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJV +UzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElT +UkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVT +MSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNS +RyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0H +ttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppb +d9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtF +cP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5 +U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- + +HiPKI Root CA - G1 +================== +-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xGzAZBgNVBAMMEkhpUEtJ +IFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRaFw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYT +AlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kg +Um9vdCBDQSAtIEcxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0 +o9QwqNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twvVcg3Px+k +wJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6lZgRZq2XNdZ1AYDgr/SE +YYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnzQs7ZngyzsHeXZJzA9KMuH5UHsBffMNsA +GJZMoYFL3QRtU6M9/Aes1MU3guvklQgZKILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfd +hSi8MEyr48KxRURHH+CKFgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj +1jOXTyFjHluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDry+K4 +9a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ/W3c1pzAtH2lsN0/ +Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgMa/aOEmem8rJY5AIJEzypuxC00jBF +8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQD +AgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi +7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqcSE5XCV0vrPSl +tJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6FzaZsT0pPBWGTMpWmWSBUdGSquE +wx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9TcXzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07Q +JNBAsNB1CI69aO4I1258EHBGG3zgiLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv +5wiZqAxeJoBF1PhoL5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+Gpz +jLrFNe85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wrkkVbbiVg +hUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+vhV4nYWBSipX3tUZQ9rb +yltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQUYDksswBVLuT1sw5XxJFBAJw/6KXf6vb/ +yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R4 +=========================== +-----BEGIN CERTIFICATE----- +MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYDVQQLExtHbG9i +YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgwMTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9i +YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkW +ymOxuYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNVHQ8BAf8E +BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/+wpu+74zyTyjhNUwCgYI +KoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147bmF0774BxL4YSFlhgjICICadVGNA3jdg +UM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm +-----END CERTIFICATE----- + +GTS Root R1 +=========== +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM +f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7raKb0 +xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnWr4+w +B7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXW +nOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk +9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zq +kUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92wO1A +K/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om3xPX +V2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDW +cfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQAD +ggIBAJ+qQibbC5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuyh6f88/qBVRRi +ClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM47HLwEXWdyzRSjeZ2axfG34ar +J45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8JZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYci +NuaCp+0KueIHoI17eko8cdLiA6EfMgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5me +LMFrUKTX5hgUvYU/Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJF +fbdT6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ0E6yove+ +7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm2tIMPNuzjsmhDYAPexZ3 +FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bbbP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3 +gm3c +-----END CERTIFICATE----- + +GTS Root R2 +=========== +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv +CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo7JUl +e3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWIm8Wb +a96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS ++LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7M +kogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJG +r61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RWIr9q +S34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73VululycslaVNV +J1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy5okL +dWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQAD +ggIBAB/Kzt3HvqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 +0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyCB19m3H0Q/gxh +swWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2uNmSRXbBoGOqKYcl3qJfEycel +/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMgyALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVn +jWQye+mew4K6Ki3pHrTgSAai/GevHyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y5 +9PYjJbigapordwj6xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M +7YNRTOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924SgJPFI/2R8 +0L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV7LXTWtiBmelDGDfrs7vR +WGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjW +HYbL +-----END CERTIFICATE----- + +GTS Root R3 +=========== +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi +MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMw +HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ +R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjO +PQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout +736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24CejQjBA +MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP0/Eq +Er24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azT +L818+FsuVbu/3ZL3pAzcMeGiAjEA/JdmZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV +11RZt+cRLInUue4X +-----END CERTIFICATE----- + +GTS Root R4 +=========== +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi +MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQw +HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ +R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjO +PQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu +hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqjQjBA +MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV2Py1 +PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/C +r8deVl5c1RxYIigL9zC2L7F8AjEA8GE8p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh +4rsUecrNIdSUtUlD +-----END CERTIFICATE----- + +Telia Root CA v2 +================ +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQxCzAJBgNVBAYT +AkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2 +MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQK +DBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ7 +6zBqAMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9vVYiQJ3q +9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9lRdU2HhE8Qx3FZLgmEKn +pNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTODn3WhUidhOPFZPY5Q4L15POdslv5e2QJl +tI5c0BE0312/UqeBAMN/mUWZFdUXyApT7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW +5olWK8jjfN7j/4nlNW4o6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNr +RBH0pUPCTEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6WT0E +BXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63RDolUK5X6wK0dmBR4 +M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZIpEYslOqodmJHixBTB0hXbOKSTbau +BcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGjYzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7W +xy+G2CQ5MB0GA1UdDgQWBBRyrOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ +8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi0f6X+J8wfBj5 +tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMMA8iZGok1GTzTyVR8qPAs5m4H +eW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBSSRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+C +y748fdHif64W1lZYudogsYMVoe+KTTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygC +QMez2P2ccGrGKMOF6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15 +h2Er3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMtTy3EHD70 +sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pTVmBds9hCG1xLEooc6+t9 +xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAWysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQ +raVplI/owd8k+BsHMYeB2F326CjYSlKArBPuUBQemMc= +-----END CERTIFICATE----- + +D-TRUST BR Root CA 1 2020 +========================= +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQswCQYDVQQGEwJE +RTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEJSIFJvb3QgQ0EgMSAy +MDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNV +BAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7 +dPYSzuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0QVK5buXu +QqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/VbNafAkl1bK6CKBrqx9t +MA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6gPKA6hjhodHRwOi8vY3JsLmQtdHJ1c3Qu +bmV0L2NybC9kLXRydXN0X2JyX3Jvb3RfY2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxP +PUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQD +AwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFWwKrY7RjEsK70Pvom +AjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHVdWNbFJWcHwHP2NVypw87 +-----END CERTIFICATE----- + +D-TRUST EV Root CA 1 2020 +========================= +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQswCQYDVQQGEwJE +RTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEVWIFJvb3QgQ0EgMSAy +MDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNV +BAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8 +ZRCC/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rDwpdhQntJ +raOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3OqQo5FD4pPfsazK2/umL +MA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6gPKA6hjhodHRwOi8vY3JsLmQtdHJ1c3Qu +bmV0L2NybC9kLXRydXN0X2V2X3Jvb3RfY2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxP +PUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQD +AwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CAy/m0sRtW9XLS/BnR +AjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJbgfM0agPnIjhQW+0ZT0MW +-----END CERTIFICATE----- + +DigiCert TLS ECC P384 Root G5 +============================= +-----BEGIN CERTIFICATE----- +MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURpZ2lDZXJ0IFRMUyBFQ0MgUDM4 +NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQg +Um9vdCBHNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1Tzvd +lHJS7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp0zVozptj +n4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICISB4CIfBFqMA4GA1UdDwEB +/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQCJao1H5+z8blUD2Wds +Jk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQLgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIx +AJSdYsiJvRmEFOml+wG4DXZDjC5Ty3zfDBeWUA== +-----END CERTIFICATE----- + +DigiCert TLS RSA4096 Root G5 +============================ +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBNMQswCQYDVQQG +EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0 +MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcNNDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2 +IFJvb3QgRzUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS8 +7IE+ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG02C+JFvuU +AT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgpwgscONyfMXdcvyej/Ces +tyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZMpG2T6T867jp8nVid9E6P/DsjyG244gXa +zOvswzH016cpVIDPRFtMbzCe88zdH5RDnU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnV +DdXifBBiqmvwPXbzP6PosMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9q +TXeXAaDxZre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cdLvvy +z6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvXKyY//SovcfXWJL5/ +MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNeXoVPzthwiHvOAbWWl9fNff2C+MIk +wcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPLtgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4E +FgQUUTMc7TZArxfTJc1paPKvTiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw +GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7HPNtQOa27PShN +lnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLFO4uJ+DQtpBflF+aZfTCIITfN +MBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQREtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/ +u4cnYiWB39yhL/btp/96j1EuMPikAdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9G +OUrYU9DzLjtxpdRv/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh +47a+p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilwMUc/dNAU +FvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WFqUITVuwhd4GTWgzqltlJ +yqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCKovfepEWFJqgejF0pW8hL2JpqA15w8oVP +bEtoL8pU9ozaMv7Da4M/OMZ+ +-----END CERTIFICATE----- + +Certainly Root R1 +================= +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAwPTELMAkGA1UE +BhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2VydGFpbmx5IFJvb3QgUjEwHhcN +MjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2Vy +dGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBANA21B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O +5MQTvqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbedaFySpvXl +8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b01C7jcvk2xusVtyWMOvwl +DbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGI +XsXwClTNSaa/ApzSRKft43jvRl5tcdF5cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkN +KPl6I7ENPT2a/Z2B7yyQwHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQ +AjeZjOVJ6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA2Cnb +rlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyHWyf5QBGenDPBt+U1 +VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMReiFPCyEQtkA6qyI6BJyLm4SGcprS +p6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBTgqj8ljZ9EXME66C6ud0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAsz +HQNTVfSVcOQrPbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d +8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi1wrykXprOQ4v +MMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrdrRT90+7iIgXr0PK3aBLXWopB +GsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9ditaY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+ +gjwN/KUD+nsa2UUeYNrEjvn8K8l7lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgH +JBu6haEaBQmAupVjyTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7 +fpYnKx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLyyCwzk5Iw +x06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5nwXARPbv0+Em34yaXOp/S +X3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6OV+KmalBWQewLK8= +-----END CERTIFICATE----- + +Certainly Root E1 +================= +-----BEGIN CERTIFICATE----- +MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQswCQYDVQQGEwJV +UzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBFMTAeFw0yMTA0 +MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlu +bHkxGjAYBgNVBAMTEUNlcnRhaW5seSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4 +fxzf7flHh4axpMCK+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9 +YBk2QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4hevIIgcwCgYIKoZIzj0E +AwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozmut6Dacpps6kFtZaSF4fC0urQe87YQVt8 +rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR +-----END CERTIFICATE----- + +E-Tugra Global Root CA RSA v3 +============================= +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +E-Tugra Global Root CA ECC v3 +============================= +-----BEGIN CERTIFICATE----- +MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMwgYAxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAbBgNV +BAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENB +IEVDQyB2MzAeFw0yMDAzMTgwOTQ2NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEP +MA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1 +Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBFQ0MgdjMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQKczLWYHMjLiSF4mDKpL2 +w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YKfWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31 +Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQ +zPUwHQYDVR0OBBYEFP+CMXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO +PQQDAwNpADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/67W4W +Aie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFxvmjkI6TZraE3 +-----END CERTIFICATE----- + +Security Communication RootCA3 +============================== +-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNVBAYTAkpQMSUw +IwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQDEx5TZWN1cml0eSBD +b21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQsw +CQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UE +AxMeU2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4rCmDvu20r +hvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzAlrenfna84xtSGc4RHwsE +NPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MGTfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2 +/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF79+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGm +npjKIG58u4iFW/vAEGK78vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtY +XLVqAvO4g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3weGVPK +p7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst+3A7caoreyYn8xrC +3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M0V9hvqG8OmpI6iZVIhZdXw3/JzOf +GAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQT9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0Vcw +CBEF/VfR2ccCAwEAAaNCMEAwHQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS +YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PAFNr0Y/Dq9HHu +Tofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd9XbXv8S2gVj/yP9kaWJ5rW4O +H3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQIUYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASx +YfQAW0q3nHE3GYV5v4GwxxMOdnE+OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZ +XSEIx2C/pHF7uNkegr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml ++LLfiAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUVnuiZIesn +KwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD2NCcnWXL0CsnMQMeNuE9 +dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI//1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm +6Vwdp6POXiUyK+OVrCoHzrQoeIY8LaadTdJ0MN1kURXbg4NR16/9M51NZg== +-----END CERTIFICATE----- + +Security Communication ECC RootCA1 +================================== +-----BEGIN CERTIFICATE----- +MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYTAkpQMSUwIwYD +VQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYDVQQDEyJTZWN1cml0eSBDb21t +dW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYxNjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTEL +MAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNV +BAMTIlNlY3VyaXR5IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+CnnfdldB9sELLo +5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpKULGjQjBAMB0GA1UdDgQW +BBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAK +BggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3L +snNdo4gIxwwCMQDAqy0Obe0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70e +N9k= +-----END CERTIFICATE----- + +BJCA Global Root CA1 +==================== +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQG +EwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJVFkxHTAbBgNVBAMMFEJK +Q0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAzMTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkG +A1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQD +DBRCSkNBIEdsb2JhbCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFm +CL3ZxRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZspDyRhyS +sTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O558dnJCNPYwpj9mZ9S1Wn +P3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgRat7GGPZHOiJBhyL8xIkoVNiMpTAK+BcW +yqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRj +eulumijWML3mG90Vr4TqnMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNn +MoH1V6XKV0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/pj+b +OT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZOz2nxbkRs1CTqjSSh +GL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXnjSXWgXSHRtQpdaJCbPdzied9v3pK +H9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMB +AAGjQjBAMB0GA1UdDgQWBBTF7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 +YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3KliawLwQ8hOnThJ +dMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u+2D2/VnGKhs/I0qUJDAnyIm8 +60Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuh +TaRjAv04l5U/BXCga99igUOLtFkNSoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW +4AB+dAb/OMRyHdOoP2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmp +GQrI+pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRzznfSxqxx +4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9eVzYH6Eze9mCUAyTF6ps +3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4S +SPfSKcOYKMryMguTjClPPGAyzQWWYezyr/6zcCwupvI= +-----END CERTIFICATE----- + +BJCA Global Root CA2 +==================== +-----BEGIN CERTIFICATE----- +MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQswCQYDVQQGEwJD +TjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJVFkxHTAbBgNVBAMMFEJKQ0Eg +R2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgyMVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UE +BhMCQ04xJjAkBgNVBAoMHUJFSUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRC +SkNBIEdsb2JhbCBSb290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jl +SR9BIgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK++kpRuDCK +/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJKsVF/BvDRgh9Obl+rg/xI +1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8 +W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8g +UXOQwKhbYdDFUDn9hf7B43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== +-----END CERTIFICATE----- From 41135a612a58a5024ca4c66072e9245e90b02ecc Mon Sep 17 00:00:00 2001 From: Adam Simmons Date: Wed, 5 Jul 2023 19:12:15 -0500 Subject: [PATCH 10/13] Disable video/audio by default. --- Jenkinsfile | 6 +++--- cmake/build_options.cmake | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 592771cfd..275fea316 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -28,7 +28,7 @@ pipeline { # Build Release mkdir -p build cd build - cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DUL_GENERATE_SDK=1 + cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DUL_GENERATE_SDK=1 -DUL_ENABLE_VIDEO=0 ninja cd .. ''' @@ -55,7 +55,7 @@ pipeline { rem Build Release if not exist build mkdir build cd build - cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DUWP_PLATFORM=0 -DWINDOWS_DESKTOP_PLATFORM=1 -DUL_GENERATE_SDK=1 + cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DUWP_PLATFORM=0 -DWINDOWS_DESKTOP_PLATFORM=1 -DUL_GENERATE_SDK=1 -DUL_ENABLE_VIDEO=0 ninja cd .. ''' @@ -87,7 +87,7 @@ pipeline { cd build export CC=/usr/bin/clang export CXX=/usr/bin/clang++ - cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DUL_GENERATE_SDK=1 + cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DUL_GENERATE_SDK=1 -DUL_ENABLE_VIDEO=0 ninja cd .. ''' diff --git a/cmake/build_options.cmake b/cmake/build_options.cmake index f68518c4c..abdfdc378 100644 --- a/cmake/build_options.cmake +++ b/cmake/build_options.cmake @@ -3,7 +3,7 @@ set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/out CACHE PATH "The pat set(ALLINONE_BUILD OFF CACHE BOOL "Whether or not we are building all modules monolithically.") set(UL_GENERATE_SDK OFF CACHE BOOL "Whether or not to generate a compressed SDK package from the build.") set(UL_ENABLE_STATIC_BUILD OFF CACHE BOOL "Whether or not to link the library statically (otherwise, will be linked dynamically).") -set(UL_ENABLE_VIDEO ON CACHE BOOL "Whether or not to enable video/audio via GStreamer/FFmpeg.") +set(UL_ENABLE_VIDEO OFF CACHE BOOL "Whether or not to enable video/audio via GStreamer/FFmpeg.") set(UL_ENABLE_JIT ON CACHE BOOL "Whether or not to enable JavaScript Just-In-Time compilation.") set(UL_ENABLE_MIMALLOC ON CACHE BOOL "Whether or not to enable the high-performance mi-malloc allocator.") set(UL_ENABLE_TESTS ON CACHE BOOL "Whether or not to enable test applications.") From c337f4167485ca9460fae4b3061af00809459ba3 Mon Sep 17 00:00:00 2001 From: Adam Simmons Date: Wed, 5 Jul 2023 19:12:26 -0500 Subject: [PATCH 11/13] Update deps. --- Deps.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Deps.cmake b/Deps.cmake index 2dc5b01d7..a26f6a554 100644 --- a/Deps.cmake +++ b/Deps.cmake @@ -21,7 +21,7 @@ endif () set(WEBCORE_DEPS_REV "df325984") set(GSTREAMER_REV "e78cae57") -set(ULTRALIGHTCORE_REV "07737e9d") +set(ULTRALIGHTCORE_REV "c870cd37") if (${ALLINONE_BUILD}) message("Using local deps from all-in-one build.") From d838c7a31532fa00250793481b4a471946208677 Mon Sep 17 00:00:00 2001 From: Adam Simmons Date: Tue, 11 Jul 2023 16:50:47 -0500 Subject: [PATCH 12/13] Fix issue drawing gradients with CPU renderer. --- Deps.cmake | 2 +- Source/WebCore/platform/graphics/GradientImage.cpp | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Deps.cmake b/Deps.cmake index a26f6a554..8302304cc 100644 --- a/Deps.cmake +++ b/Deps.cmake @@ -21,7 +21,7 @@ endif () set(WEBCORE_DEPS_REV "df325984") set(GSTREAMER_REV "e78cae57") -set(ULTRALIGHTCORE_REV "c870cd37") +set(ULTRALIGHTCORE_REV "41a04e01") if (${ALLINONE_BUILD}) message("Using local deps from all-in-one build.") diff --git a/Source/WebCore/platform/graphics/GradientImage.cpp b/Source/WebCore/platform/graphics/GradientImage.cpp index c6c538c8d..3748207d2 100644 --- a/Source/WebCore/platform/graphics/GradientImage.cpp +++ b/Source/WebCore/platform/graphics/GradientImage.cpp @@ -68,12 +68,9 @@ void GradientImage::drawPattern(GraphicsContext& destContext, const FloatRect& d double xScale = fabs(destContextCTM.xScale()); double yScale = fabs(destContextCTM.yScale()); #if USE(ULTRALIGHT) - // FIXME: Disable upscaling when GPU-backed Canvas is being used due to shader transform math issues - auto platformContext = destContext.platformContext(); - if (!platformContext->canvas()->surface()) { - xScale = 1.0; - yScale = 1.0; - } + // FIXME: Disable upscaling due to transform math issues + xScale = 1.0; + yScale = 1.0; #endif AffineTransform adjustedPatternCTM = patternTransform; adjustedPatternCTM.scale(1.0 / xScale, 1.0 / yScale); From dbe4e2e29d5b0b5bbe4161efa56be667728348db Mon Sep 17 00:00:00 2001 From: tabudz Date: Mon, 22 Dec 2025 22:38:06 +0800 Subject: [PATCH 13/13] Avoid drawing SVG image content when the image is of zero size. R=pdr BUG=330420 Review URL: https://codereview.chromium.org/109753004 git-svn-id: svn://svn.chromium.org/blink/trunk@164536 bbb929c8-8fbe-4397-9dbb-9b2b20218538 --- Source/WebCore/rendering/svg/RenderSVGImage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/WebCore/rendering/svg/RenderSVGImage.cpp b/Source/WebCore/rendering/svg/RenderSVGImage.cpp index 2c6f3178e..982dcd27b 100644 --- a/Source/WebCore/rendering/svg/RenderSVGImage.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGImage.cpp @@ -150,7 +150,7 @@ void RenderSVGImage::paint(PaintInfo& paintInfo, const LayoutPoint&) GraphicsContextStateSaver stateSaver(childPaintInfo.context()); childPaintInfo.applyTransform(m_localTransform); - if (childPaintInfo.phase == PaintPhase::Foreground) { + if (childPaintInfo.phase == PaintPhase::Foreground && !m_objectBoundingBox.isEmpty()) { SVGRenderingContext renderingContext(*this, childPaintInfo); if (renderingContext.isRenderingPrepared()) {