Merge branch 'dev-win' into dev

This commit is contained in:
daan 2019-08-10 15:56:26 -07:00
commit 49b1f55029
35 changed files with 992 additions and 452 deletions

2
.gitattributes vendored
View file

@ -6,3 +6,5 @@
*.suo binary *.suo binary
*.vcproj binary *.vcproj binary
*.patch binary *.patch binary
*.dll binary
*.lib binary

View file

@ -26,7 +26,6 @@ set(mi_sources
src/options.c src/options.c
src/init.c) src/init.c)
# Set default build type # Set default build type
if (NOT CMAKE_BUILD_TYPE) if (NOT CMAKE_BUILD_TYPE)
if ("${CMAKE_BINARY_DIR}" MATCHES ".*(D|d)ebug$") if ("${CMAKE_BINARY_DIR}" MATCHES ".*(D|d)ebug$")
@ -44,6 +43,11 @@ if("${CMAKE_BINARY_DIR}" MATCHES ".*(S|s)ecure$")
set(MI_SECURE "ON") set(MI_SECURE "ON")
endif() endif()
if(CMAKE_C_COMPILER_ID MATCHES "MSVC")
set(MI_USE_CXX "ON")
endif()
# Options # Options
if(MI_OVERRIDE MATCHES "ON") if(MI_OVERRIDE MATCHES "ON")
message(STATUS "Override standard malloc (MI_OVERRIDE=ON)") message(STATUS "Override standard malloc (MI_OVERRIDE=ON)")
@ -78,7 +82,7 @@ endif()
if(MI_USE_CXX MATCHES "ON") if(MI_USE_CXX MATCHES "ON")
message(STATUS "Use the C++ compiler to compile (MI_USE_CXX=ON)") 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(${mi_sources} PROPERTIES LANGUAGE CXX )
set_source_files_properties(src/static.c test/test-api.c PROPERTIES LANGUAGE CXX ) set_source_files_properties(src/static.c test/test-api.c test/test-stress.c PROPERTIES LANGUAGE CXX )
endif() endif()
# Compiler flags # Compiler flags
@ -87,6 +91,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU")
if(CMAKE_C_COMPILER_ID MATCHES "GNU") if(CMAKE_C_COMPILER_ID MATCHES "GNU")
list(APPEND mi_cflags -Wno-invalid-memory-model) list(APPEND mi_cflags -Wno-invalid-memory-model)
list(APPEND mi_cflags -fvisibility=hidden) list(APPEND mi_cflags -fvisibility=hidden)
list(APPEND mi_cflags -fbranch-target-load-optimize )
endif() endif()
endif() endif()
@ -120,14 +125,28 @@ add_library(mimalloc SHARED ${mi_sources})
set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} NO_SONAME "YES" OUTPUT_NAME ${mi_basename} ) set_target_properties(mimalloc PROPERTIES VERSION ${mi_version} NO_SONAME "YES" OUTPUT_NAME ${mi_basename} )
target_compile_definitions(mimalloc PRIVATE ${mi_defines} MI_SHARED_LIB MI_SHARED_LIB_EXPORT) target_compile_definitions(mimalloc PRIVATE ${mi_defines} MI_SHARED_LIB MI_SHARED_LIB_EXPORT)
target_compile_options(mimalloc PRIVATE ${mi_cflags}) target_compile_options(mimalloc PRIVATE ${mi_cflags})
target_link_libraries(mimalloc PUBLIC ${mi_libraries})
target_include_directories(mimalloc PUBLIC target_include_directories(mimalloc PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_dir}/include> $<INSTALL_INTERFACE:${mi_install_dir}/include>
) )
target_link_libraries(mimalloc PUBLIC ${mi_libraries}) if(WIN32)
# On windows copy the mimalloc redirection dll too.
target_link_libraries(mimalloc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect.lib)
add_custom_command(TARGET mimalloc POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/bin/mimalloc-redirect.dll" $<TARGET_FILE_DIR:mimalloc>
COMMENT "Copy mimalloc-redirect.dll to output directory")
endif()
# static library # static library
add_library(mimalloc-static STATIC ${mi_sources}) add_library(mimalloc-static STATIC ${mi_sources})
target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB)
target_compile_options(mimalloc-static PRIVATE ${mi_cflags})
target_link_libraries(mimalloc-static PUBLIC ${mi_libraries})
target_include_directories(mimalloc-static PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_dir}/include>
)
if(WIN32) if(WIN32)
# When building both static and shared libraries on Windows, a static library should use a # 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. # different output name to avoid the conflict with the import library of a shared one.
@ -136,19 +155,12 @@ if(WIN32)
else() else()
set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_basename}) set_target_properties(mimalloc-static PROPERTIES OUTPUT_NAME ${mi_basename})
endif() endif()
target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB)
target_compile_options(mimalloc-static PRIVATE ${mi_cflags})
target_include_directories(mimalloc-static PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_dir}/include>
)
target_link_libraries(mimalloc-static PUBLIC ${mi_libraries})
# install static and shared library, and the include files # install static and shared library, and the include files
install(TARGETS mimalloc EXPORT mimalloc DESTINATION ${mi_install_dir} LIBRARY NAMELINK_SKIP) install(TARGETS mimalloc EXPORT mimalloc DESTINATION ${mi_install_dir} LIBRARY NAMELINK_SKIP)
install(TARGETS mimalloc-static EXPORT mimalloc DESTINATION ${mi_install_dir}) install(TARGETS mimalloc-static EXPORT mimalloc DESTINATION ${mi_install_dir})
install(FILES include/mimalloc.h DESTINATION ${mi_install_dir}/include) install(FILES include/mimalloc.h DESTINATION ${mi_install_dir}/include)
install(FILES include/mimalloc-override.h DESTINATION ${mi_install_dir}/include)
install(FILES cmake/mimalloc-config.cmake DESTINATION ${mi_install_dir}/cmake) install(FILES cmake/mimalloc-config.cmake DESTINATION ${mi_install_dir}/cmake)
install(FILES cmake/mimalloc-config-version.cmake DESTINATION ${mi_install_dir}/cmake) install(FILES cmake/mimalloc-config-version.cmake DESTINATION ${mi_install_dir}/cmake)
install(EXPORT mimalloc DESTINATION ${mi_install_dir}/cmake) install(EXPORT mimalloc DESTINATION ${mi_install_dir}/cmake)

BIN
bin/mimalloc-redirect.dll Normal file

Binary file not shown.

BIN
bin/mimalloc-redirect.lib Normal file

Binary file not shown.

View file

@ -90,10 +90,18 @@
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<ExceptionHandling>false</ExceptionHandling>
<CompileAs>Default</CompileAs>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link> </Link>
<PostBuildEvent />
<PostBuildEvent>
<Command>COPY /Y $(SolutionDir)..\..\bin\mimalloc-redirect32.dll $(OutputPath)</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
@ -103,14 +111,20 @@
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\..\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<ExceptionHandling>false</ExceptionHandling>
<CompileAs>Default</CompileAs>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<EntryPointSymbol> <EntryPointSymbol>
</EntryPointSymbol> </EntryPointSymbol>
<AdditionalDependencies>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)</AdditionalDependencies> <AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
</Link> </Link>
<PostBuildEvent />
<PostBuildEvent>
<Command>COPY /Y $(SolutionDir)..\..\bin\mimalloc-redirect.dll $(OutputPath)</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <ClCompile>
@ -128,7 +142,11 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<AdditionalDependencies>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)</AdditionalDependencies>
</Link> </Link>
<PostBuildEvent>
<Command>COPY /Y $(SolutionDir)..\..\bin\mimalloc-redirect32.dll $(OutputPath)</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <ClCompile>
@ -150,15 +168,18 @@
</EntryPointSymbol> </EntryPointSymbol>
<AdditionalDependencies>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)</AdditionalDependencies> <AdditionalDependencies>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)</AdditionalDependencies>
</Link> </Link>
<PostBuildEvent>
<Command>COPY /Y $(SolutionDir)..\..\bin\mimalloc-redirect.dll $(OutputPath)</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\test\main-override.cpp" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="mimalloc-override.vcxproj"> <ProjectReference Include="mimalloc-override.vcxproj">
<Project>{abb5eae7-b3e6-432e-b636-333449892ea7}</Project> <Project>{abb5eae7-b3e6-432e-b636-333449892ea7}</Project>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\test\main-override.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>

View file

@ -35,7 +35,6 @@
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
@ -46,7 +45,6 @@
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">
@ -70,21 +68,25 @@
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.dll</TargetExt> <TargetExt>.dll</TargetExt>
<TargetName>mimalloc-override</TargetName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.dll</TargetExt> <TargetExt>.dll</TargetExt>
<TargetName>mimalloc-override</TargetName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.dll</TargetExt> <TargetExt>.dll</TargetExt>
<TargetName>mimalloc-override</TargetName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.dll</TargetExt> <TargetExt>.dll</TargetExt>
<TargetName>mimalloc-override</TargetName>
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile> <ClCompile>
@ -93,25 +95,20 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;_MBCS;%(PreprocessorDefinitions);</PreprocessorDefinitions> <PreprocessorDefinitions>MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<SupportJustMyCode>false</SupportJustMyCode> <SupportJustMyCode>false</SupportJustMyCode>
<CompileAs>Default</CompileAs>
</ClCompile> </ClCompile>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
<PostBuildEvent>
<Message>
</Message>
</PostBuildEvent>
<Link> <Link>
<EntryPointSymbol>DllEntry</EntryPointSymbol> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries> <IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries> </IgnoreSpecificDefaultLibraries>
<ModuleDefinitionFile> <ModuleDefinitionFile>
</ModuleDefinitionFile> </ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<EntryPointSymbol>DllEntry</EntryPointSymbol>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -121,26 +118,27 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;_MBCS;%(PreprocessorDefinitions);</PreprocessorDefinitions> <PreprocessorDefinitions>MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<SupportJustMyCode>false</SupportJustMyCode> <SupportJustMyCode>false</SupportJustMyCode>
<CompileAs>Default</CompileAs>
</ClCompile> </ClCompile>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
<PostBuildEvent>
<Message>
</Message>
</PostBuildEvent>
<Link> <Link>
<EntryPointSymbol>DllEntry</EntryPointSymbol> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries> <IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries> </IgnoreSpecificDefaultLibraries>
<ModuleDefinitionFile> <ModuleDefinitionFile>
</ModuleDefinitionFile> </ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<EntryPointSymbol>DllEntry</EntryPointSymbol>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
</Link> </Link>
<PostBuildEvent>
<Command>COPY /Y $(SolutionDir)..\..\bin\mimalloc-redirect.dll $(OutputPath)</Command>
</PostBuildEvent>
<PostBuildEvent>
<Message>copy mimalloc-redirect.dll to the output directory</Message>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <ClCompile>
@ -151,28 +149,23 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions> <PreprocessorDefinitions>MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
<AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput> <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<WholeProgramOptimization>false</WholeProgramOptimization> <WholeProgramOptimization>false</WholeProgramOptimization>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<CompileAs>Default</CompileAs>
</ClCompile> </ClCompile>
<Link> <Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<EntryPointSymbol>DllEntry</EntryPointSymbol> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile> <ModuleDefinitionFile>
</ModuleDefinitionFile> </ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<EntryPointSymbol>DllEntry</EntryPointSymbol>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
</Link> </Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
<PostBuildEvent>
<Message>
</Message>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <ClCompile>
@ -183,33 +176,35 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;_MBCS;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions> <PreprocessorDefinitions>MI_SHARED_LIB;MI_SHARED_LIB_EXPORT;MI_MALLOC_OVERRIDE;%(PreprocessorDefinitions);NDEBUG</PreprocessorDefinitions>
<AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput> <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<WholeProgramOptimization>false</WholeProgramOptimization> <WholeProgramOptimization>false</WholeProgramOptimization>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<CompileAs>Default</CompileAs>
</ClCompile> </ClCompile>
<Link> <Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<EntryPointSymbol>DllEntry</EntryPointSymbol> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile> <ModuleDefinitionFile>
</ModuleDefinitionFile> </ModuleDefinitionFile>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
<EntryPointSymbol>DllEntry</EntryPointSymbol>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command> <Command>COPY /Y $(SolutionDir)..\..\bin\mimalloc-redirect.dll $(OutputPath)</Command>
</Command>
</PostBuildEvent> </PostBuildEvent>
<PostBuildEvent> <PostBuildEvent>
<Message> <Message>copy mimalloc-redirect.dll to the output directory</Message>
</Message>
</PostBuildEvent> </PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h" /> <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h" />
<ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h" /> <ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h" />
<ClInclude Include="..\..\include\mimalloc-atomic.h" /> <ClInclude Include="..\..\include\mimalloc-atomic.h" />
<ClInclude Include="..\..\include\mimalloc-override.h" />
<ClInclude Include="..\..\include\mimalloc-types.h" /> <ClInclude Include="..\..\include\mimalloc-types.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -220,6 +215,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\alloc-override-win.c" /> <ClCompile Include="..\..\src\alloc-override-win.c" />
<ClCompile Include="..\..\src\alloc-override.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\alloc-posix.c" /> <ClCompile Include="..\..\src\alloc-posix.c" />
<ClCompile Include="..\..\src\alloc.c" /> <ClCompile Include="..\..\src\alloc.c" />
<ClCompile Include="..\..\src\heap.c" /> <ClCompile Include="..\..\src\heap.c" />

View file

@ -23,6 +23,9 @@
<ClInclude Include="..\..\include\mimalloc-atomic.h"> <ClInclude Include="..\..\include\mimalloc-atomic.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\include\mimalloc-override.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\src\alloc.c"> <ClCompile Include="..\..\src\alloc.c">
@ -46,9 +49,6 @@
<ClCompile Include="..\..\src\alloc-aligned.c"> <ClCompile Include="..\..\src\alloc-aligned.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\alloc-override-win.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\heap.c"> <ClCompile Include="..\..\src\heap.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -61,8 +61,14 @@
<ClCompile Include="..\..\src\memory.c"> <ClCompile Include="..\..\src\memory.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\alloc-override.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\alloc-posix.c"> <ClCompile Include="..\..\src\alloc-posix.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\alloc-override-win.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -67,19 +67,19 @@
</ImportGroup> </ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</OutDir> <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</OutDir> <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</OutDir> <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</OutDir> <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">

View file

@ -67,19 +67,19 @@
</ImportGroup> </ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</OutDir> <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</OutDir> <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</OutDir> <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</OutDir> <OutDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(ProjectDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@ -144,19 +144,14 @@
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\test\test-api.c">
<AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AssemblyAndSourceCode</AssemblerOutput>
<AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AssemblyAndSourceCode</AssemblerOutput>
<AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AssemblyAndSourceCode</AssemblerOutput>
<AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AssemblyAndSourceCode</AssemblerOutput>
</ClCompile>
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="mimalloc.vcxproj"> <ProjectReference Include="mimalloc.vcxproj">
<Project>{abb5eae7-b3e6-432e-b636-333449892ea6}</Project> <Project>{abb5eae7-b3e6-432e-b636-333449892ea6}</Project>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\test\main-override-static.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>

View file

@ -15,7 +15,7 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\test\test-api.c"> <ClCompile Include="..\..\test\main-override-static.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>

View file

@ -70,21 +70,25 @@
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt> <TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt> <TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt> <TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir> <OutDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir> <IntDir>$(SolutionDir)..\..\out\msvc-$(Platform)\$(ProjectName)\$(Configuration)\</IntDir>
<TargetExt>.lib</TargetExt> <TargetExt>.lib</TargetExt>
<TargetName>mimalloc-static</TargetName>
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile> <ClCompile>
@ -94,8 +98,9 @@
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>MI_DEBUG=3;%(PreprocessorDefinitions);</PreprocessorDefinitions> <PreprocessorDefinitions>MI_DEBUG=3;%(PreprocessorDefinitions);</PreprocessorDefinitions>
<CompileAs>Default</CompileAs> <CompileAs>CompileAsCpp</CompileAs>
<SupportJustMyCode>false</SupportJustMyCode> <SupportJustMyCode>false</SupportJustMyCode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<Lib> <Lib>
<AdditionalLibraryDirectories> <AdditionalLibraryDirectories>
@ -112,8 +117,9 @@
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>../../include</AdditionalIncludeDirectories>
<PreprocessorDefinitions>MI_DEBUG=3;%(PreprocessorDefinitions);</PreprocessorDefinitions> <PreprocessorDefinitions>MI_DEBUG=3;%(PreprocessorDefinitions);</PreprocessorDefinitions>
<CompileAs>Default</CompileAs> <CompileAs>CompileAsCpp</CompileAs>
<SupportJustMyCode>false</SupportJustMyCode> <SupportJustMyCode>false</SupportJustMyCode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<PostBuildEvent> <PostBuildEvent>
<Command> <Command>
@ -148,7 +154,7 @@
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed> <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<OmitFramePointers>false</OmitFramePointers> <OmitFramePointers>false</OmitFramePointers>
<EnableFiberSafeOptimizations>false</EnableFiberSafeOptimizations> <EnableFiberSafeOptimizations>false</EnableFiberSafeOptimizations>
<CompileAs>Default</CompileAs> <CompileAs>CompileAsCpp</CompileAs>
</ClCompile> </ClCompile>
<Link> <Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
@ -179,7 +185,7 @@
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed> <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<OmitFramePointers>false</OmitFramePointers> <OmitFramePointers>false</OmitFramePointers>
<EnableFiberSafeOptimizations>false</EnableFiberSafeOptimizations> <EnableFiberSafeOptimizations>false</EnableFiberSafeOptimizations>
<CompileAs>Default</CompileAs> <CompileAs>CompileAsCpp</CompileAs>
</ClCompile> </ClCompile>
<Link> <Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
@ -211,12 +217,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\alloc-override-win.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\alloc-override.c"> <ClCompile Include="..\..\src\alloc-override.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -243,8 +243,9 @@
<ItemGroup> <ItemGroup>
<ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h" /> <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h" />
<ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h" /> <ClInclude Include="$(ProjectDir)..\..\include\mimalloc.h" />
<ClInclude Include="..\..\include\mimalloc-atomic.h" /> <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-atomic.h" />
<ClInclude Include="..\..\include\mimalloc-types.h" /> <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-override.h" />
<ClInclude Include="$(ProjectDir)..\..\include\mimalloc-types.h" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View file

@ -41,9 +41,6 @@
<ClCompile Include="..\..\src\alloc-override.c"> <ClCompile Include="..\..\src\alloc-override.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\alloc-override-win.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\options.c"> <ClCompile Include="..\..\src\options.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -64,11 +61,14 @@
<ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h"> <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-internal.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\include\mimalloc-types.h"> <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-atomic.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\include\mimalloc-atomic.h"> <ClInclude Include="$(ProjectDir)..\..\include\mimalloc-override.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(ProjectDir)..\..\include\mimalloc-types.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -35,10 +35,10 @@ bool _mi_is_main_thread(void);
uintptr_t _mi_ptr_cookie(const void* p); uintptr_t _mi_ptr_cookie(const void* p);
uintptr_t _mi_random_shuffle(uintptr_t x); uintptr_t _mi_random_shuffle(uintptr_t x);
uintptr_t _mi_random_init(uintptr_t seed /* can be zero */); uintptr_t _mi_random_init(uintptr_t seed /* can be zero */);
bool _mi_preloading(); // true while the C runtime is not ready
// os.c // os.c
size_t _mi_os_page_size(void); size_t _mi_os_page_size(void);
uintptr_t _mi_align_up(uintptr_t sz, size_t alignment);
void _mi_os_init(void); // called from process init 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_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 void _mi_os_free(void* p, size_t size, mi_stats_t* stats); // to free thread local data
@ -164,6 +164,20 @@ static inline bool mi_mul_overflow(size_t size, size_t count, size_t* total) {
#endif #endif
} }
// Align upwards
static inline uintptr_t _mi_is_power_of_two(uintptr_t x) {
return ((x & (x - 1)) == 0);
}
static inline uintptr_t _mi_align_up(uintptr_t sz, size_t alignment) {
uintptr_t mask = alignment - 1;
if ((alignment & mask) == 0) { // power of two?
return ((sz + mask) & ~mask);
}
else {
return (((sz + mask)/alignment)*alignment);
}
}
// Align a byte size to a size in _machine words_, // Align a byte size to a size in _machine words_,
// i.e. byte size == `wsize*sizeof(void*)`. // i.e. byte size == `wsize*sizeof(void*)`.
static inline size_t _mi_wsize_from_size(size_t size) { static inline size_t _mi_wsize_from_size(size_t size) {
@ -293,13 +307,23 @@ static inline bool mi_page_all_used(mi_page_t* page) {
static inline bool mi_page_mostly_used(const mi_page_t* page) { static inline bool mi_page_mostly_used(const mi_page_t* page) {
if (page==NULL) return true; if (page==NULL) return true;
uint16_t frac = page->reserved / 8U; uint16_t frac = page->reserved / 8U;
return (page->reserved - page->used + page->thread_freed < frac); return (page->reserved - page->used + page->thread_freed <= frac);
} }
static inline mi_page_queue_t* mi_page_queue(const mi_heap_t* heap, size_t size) { static inline mi_page_queue_t* mi_page_queue(const mi_heap_t* heap, size_t size) {
return &((mi_heap_t*)heap)->pages[_mi_bin(size)]; return &((mi_heap_t*)heap)->pages[_mi_bin(size)];
} }
static inline uintptr_t mi_page_thread_id(const mi_page_t* page) {
return (page->flags.xthread_id << MI_PAGE_FLAGS_BITS);
}
static inline void mi_page_init_flags(mi_page_t* page, uintptr_t thread_id) {
page->flags.value = 0;
page->flags.xthread_id = (thread_id >> MI_PAGE_FLAGS_BITS);
mi_assert(page->flags.value == thread_id);
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Encoding/Decoding the free list next pointers // Encoding/Decoding the free list next pointers
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@ -323,12 +347,23 @@ static inline void mi_block_set_nextx(uintptr_t cookie, mi_block_t* block, mi_bl
} }
static inline mi_block_t* mi_block_next(mi_page_t* page, mi_block_t* block) { static inline mi_block_t* mi_block_next(mi_page_t* page, mi_block_t* block) {
#if MI_SECURE
return mi_block_nextx(page->cookie,block); return mi_block_nextx(page->cookie,block);
#else
UNUSED(page);
return mi_block_nextx(0, block);
#endif
} }
static inline void mi_block_set_next(mi_page_t* page, mi_block_t* block, mi_block_t* next) { static inline void mi_block_set_next(mi_page_t* page, mi_block_t* block, mi_block_t* next) {
#if MI_SECURE
mi_block_set_nextx(page->cookie,block,next); mi_block_set_nextx(page->cookie,block,next);
#else
UNUSED(page);
mi_block_set_nextx(0, block, next);
#endif
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Getting the thread id should be performant // Getting the thread id should be performant
// as it is called in the fast path of `_mi_free`, // as it is called in the fast path of `_mi_free`,

110
include/mimalloc-override.h Normal file
View file

@ -0,0 +1,110 @@
/* ----------------------------------------------------------------------------
Copyright (c) 2018,2019 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_OVERRIDE_H
#define MIMALLOC_OVERRIDE_H
/* ----------------------------------------------------------------------------
This header can be used to statically redirect malloc/free and new/delete
to the mimalloc variants. This can be useful if one can include this file on
each source file in a project (but be careful when using external code to
not accidentally mix pointers from different allocators).
On windows it can still be good to always try to include this header even
when dynamically overriding since this will give better performance especially
for new/delete. On Unix dynamic overriding already includes all variants so
including this header is not necessary.
-----------------------------------------------------------------------------*/
#include "mimalloc.h"
// Standard C allocation
#define malloc(n) mi_malloc(n)
#define calloc(n,c) mi_calloc(n,c)
#define realloc(p,n) mi_realloc(p,n)
#define free(p) mi_free(p)
#define strdup(s) mi_strdup(s)
#define strndup(s) mi_strndup(s)
#define realpath(f,n) mi_realpath(f,n)
// Microsoft extensions
#define _expand(p,n) mi_expand(p,n)
#define _msize(p) mi_usable_size(p)
#define _recalloc(p,n,c) mi_recalloc(p,n,c)
#define _strdup(s) mi_strdup(s)
#define _strndup(s) mi_strndup(s)
#define _wcsdup(s) (wchar_t*)mi_wcsdup((const unsigned short*)(s))
#define _mbsdup(s) mi_mbsdup(s)
#define _dupenv_s(b,n,v) mi_dupenv_s(b,n,v)
#define _wdupenv_s(b,n,v) mi_wdupenv_s((unsigned short*)(b),n,(const unsigned short*)(v))
// Various Posix and Unix variants
#define reallocf(p,n) mi_reallocf(p,n)
#define malloc_size(p) mi_usable_size(p)
#define malloc_usable_size(p) mi_usable_size(p)
#define cfree(p) mi_free(p)
#define valloc(n) mi_valloc(n)
#define pvalloc(n) mi_pvalloc(n)
#define reallocarray(p,s,n) mi_reallocarray(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)
#define _posix_memalign(p,a,n) mi_posix_memalign(p,a,n)
// Microsoft aligned variants
#define _aligned_malloc(n,a) mi_malloc_aligned(n,a)
#define _aligned_realloc(p,n,a) mi_realloc_aligned(p,n,a)
#define _aligned_recalloc(p,s,n,a) mi_aligned_recalloc(p,s,n,a)
#define _aligned_msize(p,a,o) mi_usable_size(p)
#define _aligned_free(p) mi_free(p)
#define _aligned_offset_malloc(n,a,o) mi_malloc_aligned_at(n,a,o)
#define _aligned_offset_realloc(p,n,a,o) mi_realloc_aligned_at(p,n,a,o)
#define _aligned_offset_recalloc(p,s,n,a,o) mi_recalloc_aligned_at(p,s,n,a,o)
// -----------------------------------------------------------------
// With a C++ compiler we can override all the new/delete operators
// by defining 'MIMALLOC_DEFINE_NEW_DELETE' in some source file and
// then including this header file. This is not needed when linking
// statically with the mimalloc library, but it can be more performant
// on Windows when using dynamic overiding as well.
// see <https://en.cppreference.com/w/cpp/memory/new/operator_new>
// -----------------------------------------------------------------
#if defined(__cplusplus) && defined(MIMALLOC_DEFINE_NEW_DELETE)
#include <new>
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 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); }
#if (__cplusplus >= 201402L || _MSC_VER >= 1916)
void operator delete (void* p, std::size_t n) { mi_free_size(p,n); };
void operator delete[](void* p, std::size_t n) { mi_free_size(p,n); };
#endif
#if (__cplusplus > 201402L || defined(__cpp_aligned_new))
void operator delete (void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
void operator delete[](void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
void operator delete (void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); };
void operator delete[](void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); };
void* operator new( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast<size_t>(al)); }
void* operator new[]( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast<size_t>(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<size_t>(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<size_t>(al)); }
#endif
#endif
#endif // MIMALLOC_OVERRIDE_H

View file

@ -8,7 +8,6 @@ terms of the MIT license. A copy of the license can be found in the file
#ifndef MIMALLOC_TYPES_H #ifndef MIMALLOC_TYPES_H
#define MIMALLOC_TYPES_H #define MIMALLOC_TYPES_H
#include <stdlib.h> // size_t etc.
#include <stddef.h> // ptrdiff_t #include <stddef.h> // ptrdiff_t
#include <stdint.h> // uintptr_t, uint16_t, etc #include <stdint.h> // uintptr_t, uint16_t, etc
@ -92,19 +91,19 @@ terms of the MIT license. A copy of the license can be found in the file
#define MI_MEDIUM_PAGES_PER_SEGMENT (MI_SEGMENT_SIZE/MI_MEDIUM_PAGE_SIZE) #define MI_MEDIUM_PAGES_PER_SEGMENT (MI_SEGMENT_SIZE/MI_MEDIUM_PAGE_SIZE)
#define MI_LARGE_PAGES_PER_SEGMENT (MI_SEGMENT_SIZE/MI_LARGE_PAGE_SIZE) #define MI_LARGE_PAGES_PER_SEGMENT (MI_SEGMENT_SIZE/MI_LARGE_PAGE_SIZE)
#define MI_MEDIUM_SIZE_MAX (MI_MEDIUM_PAGE_SIZE/8) // 64kb on 64-bit #define MI_MEDIUM_SIZE_MAX (MI_MEDIUM_PAGE_SIZE/4) // 128kb on 64-bit
#define MI_LARGE_SIZE_MAX (MI_LARGE_PAGE_SIZE/8) // 512kb on 64-bit #define MI_LARGE_SIZE_MAX (MI_LARGE_PAGE_SIZE/4) // 1Mb on 64-bit
#define MI_LARGE_WSIZE_MAX (MI_LARGE_SIZE_MAX>>MI_INTPTR_SHIFT) #define MI_LARGE_WSIZE_MAX (MI_LARGE_SIZE_MAX>>MI_INTPTR_SHIFT)
// Maximum number of size classes. (spaced exponentially in 16.7% increments)
#define MI_BIN_HUGE (64U)
// Minimal alignment necessary. On most platforms 16 bytes are needed // 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 `MI_INTPTR_SIZE`
#define MI_MAX_ALIGN_SIZE 16 // sizeof(max_align_t) #define MI_MAX_ALIGN_SIZE 16 // sizeof(max_align_t)
#if (MI_LARGE_WSIZE_MAX > 131072) // Maximum number of size classes. (spaced exponentially in 12.5% increments)
#define MI_BIN_HUGE (70U)
#if (MI_LARGE_WSIZE_MAX > 393216)
#error "define more bins" #error "define more bins"
#endif #endif
@ -124,18 +123,29 @@ typedef enum mi_delayed_e {
} mi_delayed_t; } mi_delayed_t;
// Use the lowest two bits of a thread id for the `in_full` and `has_aligned` flags
// This allows a single test in `mi_free` to check for unlikely cases
// (namely, non-local free, aligned free, or freeing in a full page)
#define MI_PAGE_FLAGS_BITS (2)
#define MI_PAGE_FLAGS_TID_BITS (MI_INTPTR_SIZE*8 - MI_PAGE_FLAGS_BITS)
typedef union mi_page_flags_u { typedef union mi_page_flags_u {
uint16_t value; uintptr_t value;
struct { struct {
bool has_aligned; #ifdef MI_BIG_ENDIAN
bool in_full; uintptr_t xthread_id : MI_PAGE_FLAGS_TID_BITS;
#endif
uintptr_t in_full : 1;
uintptr_t has_aligned : 1;
#ifndef MI_BIG_ENDIAN
uintptr_t xthread_id : MI_PAGE_FLAGS_TID_BITS;
#endif
}; };
} mi_page_flags_t; } mi_page_flags_t;
// Thread free list.
// We use bottom 2 bits of the pointer for mi_delayed_t flags
typedef uintptr_t mi_thread_free_t;
// Thread free list.
// We use the bottom 2 bits of the pointer for mi_delayed_t flags
typedef uintptr_t mi_thread_free_t;
// A page contains blocks of one specific size (`block_size`). // A page contains blocks of one specific size (`block_size`).
// Each page has three list of free blocks: // Each page has three list of free blocks:
@ -163,13 +173,15 @@ typedef struct mi_page_s {
bool is_committed:1; // `true` if the page virtual memory is committed bool is_committed:1; // `true` if the page virtual memory is committed
// layout like this to optimize access in `mi_malloc` and `mi_free` // layout like this to optimize access in `mi_malloc` and `mi_free`
mi_page_flags_t flags;
uint16_t capacity; // number of blocks committed uint16_t capacity; // number of blocks committed
uint16_t reserved; // number of blocks reserved in memory uint16_t reserved; // number of blocks reserved in memory
// 16 bits padding
mi_block_t* free; // list of available free blocks (`malloc` allocates from this list) mi_block_t* free; // list of available free blocks (`malloc` allocates from this list)
#if MI_SECURE
uintptr_t cookie; // random cookie to encode the free lists uintptr_t cookie; // random cookie to encode the free lists
#endif
size_t used; // number of blocks in use (including blocks in `local_free` and `thread_free`) size_t used; // number of blocks in use (including blocks in `local_free` and `thread_free`)
mi_page_flags_t flags; // threadid:62 | has_aligned:1 | in_full:1
mi_block_t* local_free; // list of deferred free blocks by this thread (migrates to `free`) mi_block_t* local_free; // list of deferred free blocks by this thread (migrates to `free`)
volatile uintptr_t thread_freed; // at least this number of blocks are in `thread_free` volatile uintptr_t thread_freed; // at least this number of blocks are in `thread_free`
@ -182,10 +194,10 @@ typedef struct mi_page_s {
struct mi_page_s* prev; // previous 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`
// improve page index calculation // improve page index calculation
#if MI_INTPTR_SIZE==8 #if (MI_INTPTR_SIZE==8 && MI_SECURE==0)
//void* padding[1]; // 10 words on 64-bit void* padding[1]; // 12 words on 64-bit
#elif MI_INTPTR_SIZE==4 #elif MI_INTPTR_SIZE==4
void* padding[1]; // 12 words on 32-bit // void* padding[1]; // 12 words on 32-bit
#endif #endif
} mi_page_t; } mi_page_t;
@ -215,7 +227,7 @@ typedef struct mi_segment_s {
// layout like this to optimize access in `mi_free` // 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`). size_t page_shift; // `1 << page_shift` == the page sizes == `page->block_size * page->reserved` (unless the first page, then `-segment_info_size`).
uintptr_t thread_id; // unique id of the thread owning this segment volatile 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_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_page_t pages[1]; // up to `MI_SMALL_PAGES_PER_SEGMENT` pages
} mi_segment_t; } mi_segment_t;
@ -324,12 +336,12 @@ typedef struct mi_stats_s {
mi_stat_count_t pages_abandoned; mi_stat_count_t pages_abandoned;
mi_stat_count_t pages_extended; mi_stat_count_t pages_extended;
mi_stat_count_t mmap_calls; mi_stat_count_t mmap_calls;
mi_stat_count_t mmap_right_align;
mi_stat_count_t mmap_ensure_aligned;
mi_stat_count_t commit_calls; mi_stat_count_t commit_calls;
mi_stat_count_t threads; mi_stat_count_t threads;
mi_stat_count_t huge; mi_stat_count_t huge;
mi_stat_count_t malloc; mi_stat_count_t malloc;
mi_stat_count_t segments_cache;
mi_stat_counter_t page_no_retire;
mi_stat_counter_t searches; mi_stat_counter_t searches;
#if MI_STAT>1 #if MI_STAT>1
mi_stat_count_t normal[MI_BIN_HUGE+1]; mi_stat_count_t normal[MI_BIN_HUGE+1];

View file

@ -52,8 +52,8 @@ 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_size2(s1,s2)
#else #else
#define mi_attr_alloc_size(s) __attribute__((alloc_size(s))) #define mi_attr_alloc_size(s) __attribute__((alloc_size(s)))
#define mi_attr_alloc_size2(s1,s2) __attribute__((alloc_size(s1,s2))) #define mi_attr_alloc_size2(s1,s2) __attribute__((alloc_size(s1,s2)))
#define mi_cdecl // leads to warnings... __attribute__((cdecl)) #define mi_cdecl // leads to warnings... __attribute__((cdecl))
#endif #endif
#else #else
#define mi_decl_thread __thread #define mi_decl_thread __thread
@ -62,14 +62,13 @@ terms of the MIT license. A copy of the license can be found in the file
#define mi_attr_malloc #define mi_attr_malloc
#define mi_attr_alloc_size(s) #define mi_attr_alloc_size(s)
#define mi_attr_alloc_size2(s1,s2) #define mi_attr_alloc_size2(s1,s2)
#define mi_cdecl #define mi_cdecl
#endif #endif
// ------------------------------------------------------ // ------------------------------------------------------
// Includes // Includes
// ------------------------------------------------------ // ------------------------------------------------------
#include <stdlib.h> // size_t, malloc etc.
#include <stdbool.h> // bool #include <stdbool.h> // bool
#include <stdio.h> // FILE #include <stdio.h> // FILE
@ -197,6 +196,7 @@ mi_decl_export bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_all_b
mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept; mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept;
// ------------------------------------------------------ // ------------------------------------------------------
// Convenience // Convenience
// ------------------------------------------------------ // ------------------------------------------------------
@ -245,14 +245,15 @@ 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); mi_decl_export void mi_option_set_default(mi_option_t option, long value);
// ---------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------
// mi prefixed implementations of various posix, unix, and C++ allocation functions. // "mi" prefixed implementations of various posix, Unix, Windows, and C++ allocation functions.
// ----------------------------------------------------------------------------------- // (This can be convenient when providing overrides of these functions as done in `mimalloc-override.h`.)
// -------------------------------------------------------------------------------------------------------
mi_decl_export void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept;
mi_decl_export size_t mi_malloc_size(const void* p) mi_attr_noexcept; mi_decl_export size_t mi_malloc_size(const void* p) mi_attr_noexcept;
mi_decl_export size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept; mi_decl_export size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept;
mi_decl_export void mi_cfree(void* p) mi_attr_noexcept; 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_export int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept; mi_decl_export int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept;
mi_decl_export int mi__posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept; mi_decl_export int mi__posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept;
@ -263,6 +264,15 @@ mi_decl_export mi_decl_allocator void* mi_pvalloc(size_t size) mi_attr_noexcept
mi_decl_export mi_decl_allocator void* mi_aligned_alloc(size_t alignment, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2); mi_decl_export mi_decl_allocator void* mi_aligned_alloc(size_t alignment, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2);
mi_decl_export mi_decl_allocator void* mi_reallocarray(void* p, size_t count, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(2,3); mi_decl_export mi_decl_allocator void* mi_reallocarray(void* p, size_t count, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size2(2,3);
mi_decl_export void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept;
mi_decl_export void* mi_aligned_recalloc(void* p, size_t size, size_t newcount, size_t alignment) mi_attr_noexcept;
mi_decl_export void* mi_aligned_offset_recalloc(void* p, size_t size, size_t newcount, size_t alignment, size_t offset) mi_attr_noexcept;
mi_decl_export unsigned short* mi_wcsdup(const unsigned short* s) mi_attr_noexcept;
mi_decl_export unsigned char* mi_mbsdup(const unsigned char* s) mi_attr_noexcept;
mi_decl_export int mi_dupenv_s(char** buf, size_t* size, const char* name) mi_attr_noexcept;
mi_decl_export int mi_wdupenv_s(unsigned short** buf, size_t* size, const unsigned short* name) mi_attr_noexcept;
mi_decl_export void mi_free_size(void* p, size_t size) mi_attr_noexcept; mi_decl_export void mi_free_size(void* p, size_t size) mi_attr_noexcept;
mi_decl_export void mi_free_size_aligned(void* p, size_t size, size_t alignment) mi_attr_noexcept; mi_decl_export void mi_free_size_aligned(void* p, size_t size, size_t alignment) mi_attr_noexcept;
mi_decl_export void mi_free_aligned(void* p, size_t alignment) mi_attr_noexcept; mi_decl_export void mi_free_aligned(void* p, size_t alignment) mi_attr_noexcept;

View file

@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file
#include "mimalloc.h" #include "mimalloc.h"
#include "mimalloc-internal.h" #include "mimalloc-internal.h"
#include <string.h> // memset #include <string.h> // memset, memcpy
// ------------------------------------------------------ // ------------------------------------------------------
// Aligned Allocation // Aligned Allocation
@ -150,3 +150,14 @@ void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t of
void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept { 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); return mi_heap_realloc_aligned(mi_get_default_heap(), p, newsize, alignment);
} }
void* mi_aligned_offset_recalloc(void* p, size_t size, size_t newcount, size_t alignment, size_t offset) mi_attr_noexcept {
size_t newsize;
if (mi_mul_overflow(size,newcount,&newsize)) return NULL;
return mi_heap_realloc_zero_aligned_at(mi_get_default_heap(), p, newsize, alignment, offset, true );
}
void* mi_aligned_recalloc(void* p, size_t size, size_t newcount, size_t alignment) mi_attr_noexcept {
size_t newsize;
if (mi_mul_overflow(size, newcount, &newsize)) return NULL;
return mi_heap_realloc_zero_aligned(mi_get_default_heap(), p, newsize, alignment, true );
}

View file

@ -15,6 +15,9 @@ terms of the MIT license. A copy of the license can be found in the file
#include <windows.h> #include <windows.h>
#include <psapi.h> #include <psapi.h>
#include <stdlib.h> // getenv
#include <string.h> // strstr
/* /*
To override the C runtime `malloc` on Windows we need to patch the allocation To override the C runtime `malloc` on Windows we need to patch the allocation
@ -98,11 +101,6 @@ static int __cdecl mi_setmaxstdio(int newmax);
// Microsoft allocation extensions // Microsoft allocation extensions
// ------------------------------------------------------ // ------------------------------------------------------
static void* mi__expand(void* p, size_t newsize) {
void* res = mi_expand(p, newsize);
if (res == NULL) errno = ENOMEM;
return res;
}
typedef size_t mi_nothrow_t; typedef size_t mi_nothrow_t;
@ -148,8 +146,6 @@ static size_t mi__msize_term(void* p) {
} }
// Debug versions, forward to base versions (that get patched)
static void* mi__malloc_dbg(size_t size, int block_type, const char* fname, int line) { static void* mi__malloc_dbg(size_t size, int block_type, const char* fname, int line) {
UNUSED(block_type); UNUSED(fname); UNUSED(line); UNUSED(block_type); UNUSED(fname); UNUSED(line);
return _malloc_base(size); return _malloc_base(size);
@ -579,7 +575,7 @@ static void mi_module_resolve(const char* fname, HMODULE mod, int priority) {
if (addr != NULL) { if (addr != NULL) {
// found it! set the address // found it! set the address
patch->originals[i] = addr; patch->originals[i] = addr;
_mi_trace_message(" override %s at %s!%p (entry %i)\n", patch->name, fname, addr, i); _mi_trace_message(" found %s at %s!%p (entry %i)\n", patch->name, fname, addr, i);
} }
} }
} }
@ -606,7 +602,6 @@ static bool mi_patches_resolve(void) {
int ucrtbase_index = 0; int ucrtbase_index = 0;
int mimalloc_index = 0; int mimalloc_index = 0;
// iterate through the loaded modules // iterate through the loaded modules
_mi_trace_message("overriding malloc dynamically...\n");
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
HMODULE mod = modules[i]; HMODULE mod = modules[i];
char filename[MAX_PATH] = { 0 }; char filename[MAX_PATH] = { 0 };
@ -680,27 +675,39 @@ __declspec(dllexport) BOOL WINAPI DllEntry(HINSTANCE inst, DWORD reason, LPVOID
mi_patches_enable_term(); mi_patches_enable_term();
} }
// C runtime main // C runtime main
BOOL ok = _DllMainCRTStartup(inst, reason, reserved); BOOL ok = _DllMainCRTStartup(inst, reason, reserved);
if (reason == DLL_PROCESS_ATTACH && ok) { if (reason == DLL_PROCESS_ATTACH && ok) {
// initialize at exit lists
mi_initialize_atexit();
// Now resolve patches // Now resolve patches
ok = mi_patches_resolve(); ok = mi_patches_resolve();
if (ok) { if (ok) {
// and register our unwind entry (this must be after resolving due to possible delayed DLL initialization from GetProcAddress) // check if patching is not disabled
mi_fls_unwind_entry = FlsAlloc(&mi_fls_unwind); #pragma warning(suppress:4996)
if (mi_fls_unwind_entry != FLS_OUT_OF_INDEXES) { const char* s = getenv("MIMALLOC_DISABLE_OVERRIDE");
FlsSetValue(mi_fls_unwind_entry, (void*)1); bool enabled = (s == NULL || !(strstr("1;TRUE;YES;ON", s) != NULL));
if (!enabled) {
_mi_verbose_message("override is disabled\n");
} }
else {
// and register our unwind entry (this must be after resolving due to possible delayed DLL initialization from GetProcAddress)
mi_fls_unwind_entry = FlsAlloc(&mi_fls_unwind);
if (mi_fls_unwind_entry != FLS_OUT_OF_INDEXES) {
FlsSetValue(mi_fls_unwind_entry, (void*)1);
}
// register our patch disabler in the global exit list // register our patch disabler in the global exit list
mi_initialize_atexit(); if (crt_atexit != NULL) (*crt_atexit)(&mi_patches_atexit);
if (crt_atexit != NULL) (*crt_atexit)(&mi_patches_atexit); if (crt_at_quick_exit != NULL) (*crt_at_quick_exit)(&mi_patches_at_quick_exit);
if (crt_at_quick_exit != NULL) (*crt_at_quick_exit)(&mi_patches_at_quick_exit);
// and patch ! this also redirects the `atexit` handling for the global exit list // and patch ! this also redirects the `atexit` handling for the global exit list
mi_patches_enable(); mi_patches_enable();
_mi_verbose_message("override is enabled\n");
// hide internal allocation // hide internal allocation
mi_stats_reset(); mi_stats_reset();
}
} }
} }
return ok; return ok;

View file

@ -10,7 +10,7 @@ terms of the MIT license. A copy of the license can be found in the file
#endif #endif
#if defined(MI_MALLOC_OVERRIDE) && defined(_WIN32) && !(defined(MI_SHARED_LIB) && defined(_DLL)) #if defined(MI_MALLOC_OVERRIDE) && defined(_WIN32) && !(defined(MI_SHARED_LIB) && defined(_DLL))
#error "It is only possible to override malloc on Windows when building as a DLL (and linking the C runtime as a DLL)" #error "It is only possible to override "malloc" on Windows when building as a 64-bit DLL (and linking the C runtime as a DLL)"
#endif #endif
#if defined(MI_MALLOC_OVERRIDE) && !defined(_WIN32) #if defined(MI_MALLOC_OVERRIDE) && !defined(_WIN32)
@ -19,10 +19,6 @@ terms of the MIT license. A copy of the license can be found in the file
// Override system malloc // Override system malloc
// ------------------------------------------------------ // ------------------------------------------------------
#if defined(_MSC_VER)
#pragma warning(disable:4273) // inconsistent dll linking
#endif
#if (defined(__GNUC__) || defined(__clang__)) && !defined(__MACH__) #if (defined(__GNUC__) || defined(__clang__)) && !defined(__MACH__)
// use aliasing to alias the exported function to one of our `mi_` functions // use aliasing to alias the exported function to one of our `mi_` functions
#if (defined(__GNUC__) && __GNUC__ >= 9) #if (defined(__GNUC__) && __GNUC__ >= 9)
@ -62,6 +58,9 @@ terms of the MIT license. A copy of the license can be found in the file
MI_INTERPOSE_MI(strdup), MI_INTERPOSE_MI(strdup),
MI_INTERPOSE_MI(strndup) MI_INTERPOSE_MI(strndup)
}; };
#elif defined(_MSC_VER)
// cannot override malloc unless using a dll.
// we just override new/delete which does work in a static library.
#else #else
// On all other systems forward to our API // On all other systems forward to our API
void* malloc(size_t size) mi_attr_noexcept MI_FORWARD1(mi_malloc, size); void* malloc(size_t size) mi_attr_noexcept MI_FORWARD1(mi_malloc, size);
@ -94,7 +93,7 @@ terms of the MIT license. A copy of the license can be found in the file
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, 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); }
#if (__cplusplus >= 201402L) #if (__cplusplus >= 201402L || _MSC_VER >= 1916)
void operator delete (void* p, std::size_t n) MI_FORWARD02(mi_free_size,p,n); void operator delete (void* p, std::size_t n) MI_FORWARD02(mi_free_size,p,n);
void operator delete[](void* p, std::size_t n) MI_FORWARD02(mi_free_size,p,n); void operator delete[](void* p, std::size_t n) MI_FORWARD02(mi_free_size,p,n);
#endif #endif
@ -194,4 +193,5 @@ int posix_memalign(void** p, size_t alignment, size_t size) { return mi_posix_me
#pragma GCC visibility pop #pragma GCC visibility pop
#endif #endif
#endif // MI_MALLOC_OVERRIDE & !_WIN32 #endif // MI_MALLOC_OVERRIDE && !_WIN32

View file

@ -18,6 +18,8 @@ terms of the MIT license. A copy of the license can be found in the file
// ------------------------------------------------------ // ------------------------------------------------------
#include <errno.h> #include <errno.h>
#include <string.h> // memcpy
#include <stdlib.h> // getenv
#ifndef EINVAL #ifndef EINVAL
#define EINVAL 22 #define EINVAL 22
@ -36,7 +38,9 @@ size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept {
} }
void mi_cfree(void* p) mi_attr_noexcept { void mi_cfree(void* p) mi_attr_noexcept {
mi_free(p); if (mi_is_in_heap_region(p)) {
mi_free(p);
}
} }
int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept { int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept {
@ -80,3 +84,68 @@ void* mi_reallocarray( void* p, size_t count, size_t size ) mi_attr_noexcept {
return newp; return newp;
} }
void* mi__expand(void* p, size_t newsize) mi_attr_noexcept { // Microsoft
void* res = mi_expand(p, newsize);
if (res == NULL) errno = ENOMEM;
return res;
}
void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept { // Microsoft
size_t total;
if (mi_mul_overflow(count, size, &total)) return NULL;
return _mi_heap_realloc_zero(mi_get_default_heap(), p, total, true);
}
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++) { }
size_t size = (len+1)*sizeof(unsigned short);
unsigned short* p = (unsigned short*)mi_malloc(size);
if (p != NULL) {
memcpy(p,s,size);
}
return p;
}
unsigned char* mi_mbsdup(const unsigned char* s) mi_attr_noexcept {
return (unsigned char*)mi_strdup((const char*)s);
}
int mi_dupenv_s(char** buf, size_t* size, const char* name) mi_attr_noexcept {
if (buf==NULL || name==NULL) return EINVAL;
if (size != NULL) *size = 0;
#pragma warning(suppress:4996)
char* p = getenv(name);
if (p==NULL) {
*buf = NULL;
}
else {
*buf = mi_strdup(p);
if (*buf==NULL) return ENOMEM;
if (size != NULL) *size = strlen(p);
}
return 0;
}
int mi_wdupenv_s(unsigned short** buf, size_t* size, const unsigned short* name) mi_attr_noexcept {
if (buf==NULL || name==NULL) return EINVAL;
if (size != NULL) *size = 0;
#if !defined(_WIN32) || (defined(WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP))
// not supported
*buf = NULL;
return EINVAL;
#else
#pragma warning(suppress:4996)
unsigned short* p = (unsigned short*)_wgetenv((const wchar_t*)name);
if (p==NULL) {
*buf = NULL;
}
else {
*buf = mi_wcsdup(p);
if (*buf==NULL) return ENOMEM;
if (size != NULL) *size = wcslen((const wchar_t*)p);
}
return 0;
#endif
}

View file

@ -8,7 +8,8 @@ terms of the MIT license. A copy of the license can be found in the file
#include "mimalloc-internal.h" #include "mimalloc-internal.h"
#include "mimalloc-atomic.h" #include "mimalloc-atomic.h"
#include <string.h> // memset #include <string.h> // memset, memcpy, strlen
#include <stdlib.h> // malloc, exit
#define MI_IN_ALLOC_C #define MI_IN_ALLOC_C
#include "alloc-override.c" #include "alloc-override.c"
@ -56,6 +57,7 @@ extern inline void* mi_malloc_small(size_t size) mi_attr_noexcept {
return mi_heap_malloc_small(mi_get_default_heap(), size); return mi_heap_malloc_small(mi_get_default_heap(), size);
} }
// zero initialized small block // zero initialized small block
void* mi_zalloc_small(size_t size) mi_attr_noexcept { void* mi_zalloc_small(size_t size) mi_attr_noexcept {
void* p = mi_malloc_small(size); void* p = mi_malloc_small(size);
@ -70,7 +72,7 @@ extern inline void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcep
void* p; void* p;
if (mi_likely(size <= MI_SMALL_SIZE_MAX)) { if (mi_likely(size <= MI_SMALL_SIZE_MAX)) {
p = mi_heap_malloc_small(heap, size); p = mi_heap_malloc_small(heap, size);
} }
else { else {
p = _mi_malloc_generic(heap, size); p = _mi_malloc_generic(heap, size);
} }
@ -198,28 +200,31 @@ static void mi_decl_noinline mi_free_generic(const mi_segment_t* segment, mi_pag
// Free a block // Free a block
void mi_free(void* p) mi_attr_noexcept void mi_free(void* p) mi_attr_noexcept
{ {
// optimize: merge null check with the segment masking (below)
//if (p == NULL) return;
#if (MI_DEBUG>0) #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("trying to free an invalid (unaligned) pointer: %p\n", p); _mi_error_message("trying to free an invalid (unaligned) pointer: %p\n", p);
return; return;
} }
#endif #endif
const mi_segment_t* const segment = _mi_ptr_segment(p); const mi_segment_t* const segment = _mi_ptr_segment(p);
if (segment == NULL) return; // checks for (p==NULL) if (segment == NULL) return; // checks for (p==NULL)
bool local = (_mi_thread_id() == segment->thread_id); // preload, note: putting the thread_id in the page->flags does not improve performance
#if (MI_DEBUG>0) #if (MI_DEBUG>0)
if (mi_unlikely(!mi_is_in_heap_region(p))) {
_mi_warning_message("possibly trying to mi_free a pointer that does not point to a valid heap region: 0x%p\n"
"(this may still be a valid very large allocation (over 64MiB))\n", p);
if (mi_likely(_mi_ptr_cookie(segment) == segment->cookie)) {
_mi_warning_message("(yes, the previous pointer 0x%p was valid after all)\n", p);
}
}
if (mi_unlikely(_mi_ptr_cookie(segment) != segment->cookie)) { if (mi_unlikely(_mi_ptr_cookie(segment) != segment->cookie)) {
_mi_error_message("trying to mi_free a pointer that does not point to a valid heap space: %p\n", p); _mi_error_message("trying to mi_free a pointer that does not point to a valid heap space: %p\n", p);
return; return;
} }
#endif #endif
mi_page_t* page = _mi_segment_page_of(segment, p); mi_page_t* page = _mi_segment_page_of(segment, p);
#if (MI_STAT>1) #if (MI_STAT>1)
@ -231,24 +236,18 @@ void mi_free(void* p) mi_attr_noexcept
// huge page stat is accounted for in `_mi_page_retire` // huge page stat is accounted for in `_mi_page_retire`
#endif #endif
// adjust if it might be an un-aligned block uintptr_t tid = _mi_thread_id();
if (mi_likely(page->flags.value==0)) { // note: merging both tests (local | value) does not matter for performance if (mi_likely(tid == page->flags.value)) {
// local, and not full or aligned
mi_block_t* block = (mi_block_t*)p; mi_block_t* block = (mi_block_t*)p;
if (mi_likely(local)) { mi_block_set_next(page, block, page->local_free);
// owning thread can free a block directly page->local_free = block;
mi_block_set_next(page, block, page->local_free); // note: moving this write earlier does not matter for performance page->used--;
page->local_free = block; if (mi_unlikely(mi_page_all_free(page))) { _mi_page_retire(page); }
page->used--;
if (mi_unlikely(mi_page_all_free(page))) { _mi_page_retire(page); }
}
else {
// use atomic operations for a multi-threaded free
_mi_free_block_mt(page, block);
}
} }
else { else {
// aligned blocks, or a full page; use the more generic path // non-local, aligned blocks, or a full page; use the more generic path
mi_free_generic(segment, page, local, p); mi_free_generic(segment, page, tid == mi_page_thread_id(page), p);
} }
} }
@ -393,12 +392,6 @@ void* mi_realloc(void* p, size_t newsize) mi_attr_noexcept {
return mi_heap_realloc(mi_get_default_heap(),p,newsize); return mi_heap_realloc(mi_get_default_heap(),p,newsize);
} }
void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept {
size_t total;
if (mi_mul_overflow(count, size, &total)) return NULL;
return _mi_heap_realloc_zero(mi_get_default_heap(),p,total,true);
}
void* mi_reallocn(void* p, size_t count, size_t size) mi_attr_noexcept { 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); return mi_heap_reallocn(mi_get_default_heap(),p,count,size);
} }
@ -467,7 +460,7 @@ char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name)
} }
} }
#else #else
#include <unistd.h> #include <unistd.h> // pathconf
static size_t mi_path_max() { static size_t mi_path_max() {
static size_t path_max = 0; static size_t path_max = 0;
if (path_max <= 0) { if (path_max <= 0) {
@ -537,6 +530,7 @@ std_new_handler_t mi_get_new_handler() {
return _ZSt15get_new_handlerv(); return _ZSt15get_new_handlerv();
} }
#else #else
// note: on windows we could dynamically link to `?get_new_handler@std@@YAP6AXXZXZ`.
std_new_handler_t mi_get_new_handler() { std_new_handler_t mi_get_new_handler() {
return NULL; return NULL;
} }

View file

@ -172,7 +172,7 @@ void mi_collect(bool force) mi_attr_noexcept {
----------------------------------------------------------- */ ----------------------------------------------------------- */
mi_heap_t* mi_heap_get_default(void) { mi_heap_t* mi_heap_get_default(void) {
mi_thread_init(); mi_thread_init();
return mi_get_default_heap(); return mi_get_default_heap();
} }

View file

@ -7,17 +7,21 @@ terms of the MIT license. A copy of the license can be found in the file
#include "mimalloc.h" #include "mimalloc.h"
#include "mimalloc-internal.h" #include "mimalloc-internal.h"
#include <string.h> // memcpy #include <string.h> // memcpy, memset
#include <stdlib.h> // atexit
// Empty page used to initialize the small free pages array // Empty page used to initialize the small free pages array
const mi_page_t _mi_page_empty = { const mi_page_t _mi_page_empty = {
0, false, false, false, {0}, 0, false, false, false, 0, 0,
0, 0, NULL, // free
NULL, 0, 0, // free, used, cookie #if MI_SECURE
0,
#endif
0, {0}, // used, flags
NULL, 0, 0, NULL, 0, 0,
0, NULL, NULL, NULL 0, NULL, NULL, NULL
#if (MI_INTPTR_SIZE==4) #if (MI_INTPTR_SIZE==8 && MI_SECURE==0)
, { NULL } , { NULL }
#endif #endif
}; };
@ -30,22 +34,23 @@ const mi_page_t _mi_page_empty = {
#define QNULL(sz) { NULL, NULL, (sz)*sizeof(uintptr_t) } #define QNULL(sz) { NULL, NULL, (sz)*sizeof(uintptr_t) }
#define MI_PAGE_QUEUES_EMPTY \ #define MI_PAGE_QUEUES_EMPTY \
{ QNULL(1), \ { QNULL(1), \
QNULL(1), QNULL(2), QNULL(3), QNULL(4), QNULL(5), QNULL(6), QNULL(7), QNULL(8), \ QNULL( 1), QNULL( 2), QNULL( 3), QNULL( 4), QNULL( 5), QNULL( 6), QNULL( 7), QNULL( 8), /* 8 */ \
QNULL(10), QNULL(12), QNULL(14), QNULL(16), QNULL(20), QNULL(24), QNULL(28), QNULL(32), \ QNULL( 10), QNULL( 12), QNULL( 14), QNULL( 16), QNULL( 20), QNULL( 24), QNULL( 28), QNULL( 32), /* 16 */ \
QNULL(40), QNULL(48), QNULL(56), QNULL(64), QNULL(80), QNULL(96), QNULL(112), QNULL(128), \ QNULL( 40), QNULL( 48), QNULL( 56), QNULL( 64), QNULL( 80), QNULL( 96), QNULL( 112), QNULL( 128), /* 24 */ \
QNULL(160), QNULL(192), QNULL(224), QNULL(256), QNULL(320), QNULL(384), QNULL(448), QNULL(512), \ QNULL( 160), QNULL( 192), QNULL( 224), QNULL( 256), QNULL( 320), QNULL( 384), QNULL( 448), QNULL( 512), /* 32 */ \
QNULL(640), QNULL(768), QNULL(896), QNULL(1024), QNULL(1280), QNULL(1536), QNULL(1792), QNULL(2048), \ QNULL( 640), QNULL( 768), QNULL( 896), QNULL( 1024), QNULL( 1280), QNULL( 1536), QNULL( 1792), QNULL( 2048), /* 40 */ \
QNULL(2560), QNULL(3072), QNULL(3584), QNULL(4096), QNULL(5120), QNULL(6144), QNULL(7168), QNULL(8192), \ QNULL( 2560), QNULL( 3072), QNULL( 3584), QNULL( 4096), QNULL( 5120), QNULL( 6144), QNULL( 7168), QNULL( 8192), /* 48 */ \
QNULL(10240), QNULL(12288), QNULL(14336), QNULL(16384), QNULL(20480), QNULL(24576), QNULL(28672), QNULL(32768), \ 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( 40960), QNULL( 49152), QNULL( 57344), QNULL( 65536), QNULL( 81920), QNULL( 98304), QNULL(114688), QNULL(131072), /* 64 */ \
QNULL(MI_LARGE_WSIZE_MAX + 1 /*131072, Huge queue */), \ QNULL(163840), QNULL(196608), QNULL(229376), QNULL(262144), QNULL(327680), /* 69 */ \
QNULL(MI_LARGE_WSIZE_MAX + 1 /* 393216, Huge queue */), \
QNULL(MI_LARGE_WSIZE_MAX + 2) /* Full queue */ } QNULL(MI_LARGE_WSIZE_MAX + 2) /* Full queue */ }
#define MI_STAT_COUNT_NULL() {0,0,0,0} #define MI_STAT_COUNT_NULL() {0,0,0,0}
// Empty statistics // Empty statistics
#if MI_STAT>1 #if MI_STAT>1
#define MI_STAT_COUNT_END_NULL() , { MI_STAT_COUNT_NULL(), MI_INIT64(MI_STAT_COUNT_NULL) } #define MI_STAT_COUNT_END_NULL() , { MI_STAT_COUNT_NULL(), MI_INIT32(MI_STAT_COUNT_NULL) }
#else #else
#define MI_STAT_COUNT_END_NULL() #define MI_STAT_COUNT_END_NULL()
#endif #endif
@ -58,7 +63,8 @@ const mi_page_t _mi_page_empty = {
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \ MI_STAT_COUNT_NULL(), \
{ 0, 0 }, \
{ 0, 0 } \ { 0, 0 } \
MI_STAT_COUNT_END_NULL() MI_STAT_COUNT_END_NULL()
@ -92,8 +98,8 @@ static mi_tld_t tld_main = {
0, 0,
&_mi_heap_main, &_mi_heap_main,
{ { NULL, NULL }, {NULL ,NULL}, 0, 0, 0, 0, 0, 0, NULL, tld_main_stats }, // segments { { NULL, NULL }, {NULL ,NULL}, 0, 0, 0, 0, 0, 0, NULL, tld_main_stats }, // segments
{ 0, NULL, NULL, 0, tld_main_stats }, // os { 0, NULL, NULL, 0, tld_main_stats }, // os
{ MI_STATS_NULL } // stats { MI_STATS_NULL } // stats
}; };
mi_heap_t _mi_heap_main = { mi_heap_t _mi_heap_main = {
@ -373,6 +379,53 @@ void mi_thread_done(void) mi_attr_noexcept {
// -------------------------------------------------------- // --------------------------------------------------------
static void mi_process_done(void); static void 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() {
return os_preloading;
}
// Communicate with the redirection module on Windows
#if 0
#ifdef __cplusplus
extern "C" {
#endif
mi_decl_export void _mi_redirect_init() {
// called on redirection
mi_redirected = true;
}
__declspec(dllimport) bool mi_allocator_init(const char** message);
__declspec(dllimport) void mi_allocator_done();
#ifdef __cplusplus
}
#endif
#else
static bool mi_allocator_init(const char** message) {
if (message != NULL) *message = NULL;
return true;
}
static void mi_allocator_done() {
// nothing to do
}
#endif
// Called once by the process loader
static void mi_process_load(void) {
os_preloading = false;
atexit(&mi_process_done);
mi_process_init();
//mi_stats_reset();
if (mi_redirected) _mi_verbose_message("malloc is redirected.\n");
// show message from the redirector (if present)
const char* msg = NULL;
mi_allocator_init(&msg);
if (msg != NULL) _mi_verbose_message(msg);
}
// Initialize the process; called by thread_init or the process loader
void mi_process_init(void) mi_attr_noexcept { void mi_process_init(void) mi_attr_noexcept {
// ensure we are called once // ensure we are called once
if (_mi_process_is_initialized) return; if (_mi_process_is_initialized) return;
@ -381,7 +434,7 @@ void mi_process_init(void) mi_attr_noexcept {
// when using dynamic linking with interpose. // when using dynamic linking with interpose.
mi_heap_t* h = _mi_heap_default; mi_heap_t* h = _mi_heap_default;
_mi_process_is_initialized = true; _mi_process_is_initialized = true;
_mi_heap_main.thread_id = _mi_thread_id(); _mi_heap_main.thread_id = _mi_thread_id();
_mi_verbose_message("process init: 0x%zx\n", _mi_heap_main.thread_id); _mi_verbose_message("process init: 0x%zx\n", _mi_heap_main.thread_id);
uintptr_t random = _mi_random_init(_mi_heap_main.thread_id) ^ (uintptr_t)h; uintptr_t random = _mi_random_init(_mi_heap_main.thread_id) ^ (uintptr_t)h;
@ -389,15 +442,16 @@ void mi_process_init(void) mi_attr_noexcept {
_mi_heap_main.cookie = (uintptr_t)&_mi_heap_main ^ random; _mi_heap_main.cookie = (uintptr_t)&_mi_heap_main ^ random;
#endif #endif
_mi_heap_main.random = _mi_random_shuffle(random); _mi_heap_main.random = _mi_random_shuffle(random);
mi_process_setup_auto_thread_done();
_mi_os_init();
#if (MI_DEBUG) #if (MI_DEBUG)
_mi_verbose_message("debug level : %d\n", MI_DEBUG); _mi_verbose_message("debug level : %d\n", MI_DEBUG);
#endif #endif
atexit(&mi_process_done); mi_thread_init();
mi_process_setup_auto_thread_done(); mi_stats_reset(); // only call stat reset *after* thread init (or the heap tld == NULL)
mi_stats_reset();
_mi_os_init();
} }
// Called when the process is done (through `at_exit`)
static void mi_process_done(void) { static void mi_process_done(void) {
// only shutdown if we were initialized // only shutdown if we were initialized
if (!_mi_process_is_initialized) return; if (!_mi_process_is_initialized) return;
@ -413,7 +467,9 @@ static void mi_process_done(void) {
mi_option_is_enabled(mi_option_verbose)) { mi_option_is_enabled(mi_option_verbose)) {
mi_stats_print(NULL); mi_stats_print(NULL);
} }
mi_allocator_done();
_mi_verbose_message("process done: 0x%zx\n", _mi_heap_main.thread_id); _mi_verbose_message("process done: 0x%zx\n", _mi_heap_main.thread_id);
os_preloading = true; // don't call the C runtime anymore
} }
@ -425,8 +481,8 @@ static void mi_process_done(void) {
__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) { __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
UNUSED(reserved); UNUSED(reserved);
UNUSED(inst); UNUSED(inst);
if (reason==DLL_PROCESS_ATTACH) { if (reason==DLL_PROCESS_ATTACH) {
mi_process_init(); mi_process_load();
} }
else if (reason==DLL_THREAD_DETACH) { else if (reason==DLL_THREAD_DETACH) {
mi_thread_done(); mi_thread_done();
@ -437,7 +493,7 @@ static void mi_process_done(void) {
#elif defined(__cplusplus) #elif defined(__cplusplus)
// C++: use static initialization to detect process start // C++: use static initialization to detect process start
static bool _mi_process_init(void) { static bool _mi_process_init(void) {
mi_process_init(); mi_process_load();
return (_mi_heap_main.thread_id != 0); return (_mi_heap_main.thread_id != 0);
} }
static bool mi_initialized = _mi_process_init(); static bool mi_initialized = _mi_process_init();
@ -445,14 +501,14 @@ static void mi_process_done(void) {
#elif defined(__GNUC__) || defined(__clang__) #elif defined(__GNUC__) || defined(__clang__)
// GCC,Clang: use the constructor attribute // GCC,Clang: use the constructor attribute
static void __attribute__((constructor)) _mi_process_init(void) { static void __attribute__((constructor)) _mi_process_init(void) {
mi_process_init(); mi_process_load();
} }
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
// MSVC: use data section magic for static libraries // MSVC: use data section magic for static libraries
// See <https://www.codeguru.com/cpp/misc/misc/applicationcontrol/article.php/c6945/Running-Code-Before-and-After-Main.htm> // See <https://www.codeguru.com/cpp/misc/misc/applicationcontrol/article.php/c6945/Running-Code-Before-and-After-Main.htm>
static int _mi_process_init(void) { static int _mi_process_init(void) {
mi_process_init(); mi_process_load();
return 0; return 0;
} }
typedef int(*_crt_cb)(void); typedef int(*_crt_cb)(void);
@ -467,5 +523,5 @@ static void mi_process_done(void) {
#pragma data_seg() #pragma data_seg()
#else #else
#pragma message("define a way to call mi_process_init/done on your platform") #pragma message("define a way to call mi_process_load on your platform")
#endif #endif

View file

@ -105,7 +105,8 @@ static size_t mi_good_commit_size(size_t size) {
} }
// Return if a pointer points into a region reserved by us. // Return if a pointer points into a region reserved by us.
bool mi_is_in_heap_region(const void* p) { bool mi_is_in_heap_region(const void* p) mi_attr_noexcept {
if (p==NULL) return false;
size_t count = mi_atomic_read(&regions_count); size_t count = mi_atomic_read(&regions_count);
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
uint8_t* start = (uint8_t*)mi_atomic_read_ptr(&regions[i].start); uint8_t* start = (uint8_t*)mi_atomic_read_ptr(&regions[i].start);
@ -114,6 +115,7 @@ bool mi_is_in_heap_region(const void* p) {
return false; return false;
} }
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
Commit from a region Commit from a region
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/

View file

@ -6,9 +6,11 @@ terms of the MIT license. A copy of the license can be found in the file
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
#include "mimalloc.h" #include "mimalloc.h"
#include "mimalloc-internal.h" #include "mimalloc-internal.h"
#include "mimalloc-atomic.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> // strcmp #include <stdlib.h> // strtol
#include <string.h> // strncpy, strncat, strlen, strstr
#include <ctype.h> // toupper #include <ctype.h> // toupper
#include <stdarg.h> #include <stdarg.h>
@ -16,6 +18,10 @@ int mi_version(void) mi_attr_noexcept {
return MI_MALLOC_VERSION; return MI_MALLOC_VERSION;
} }
#ifdef _WIN32
#include <conio.h>
#endif
// -------------------------------------------------------- // --------------------------------------------------------
// Options // Options
// -------------------------------------------------------- // --------------------------------------------------------
@ -102,16 +108,37 @@ void mi_option_enable_default(mi_option_t option, bool enable) {
// -------------------------------------------------------- // --------------------------------------------------------
// Messages // Messages
// -------------------------------------------------------- // --------------------------------------------------------
#define MAX_ERROR_COUNT (10)
static uintptr_t error_count = 0; // when MAX_ERROR_COUNT stop emitting errors and warnings
// When overriding malloc, we may recurse into mi_vfprintf if an allocation
// inside the C runtime causes another message.
static mi_decl_thread bool recurse = false;
// Define our own limited `fprintf` that avoids memory allocation. // Define our own limited `fprintf` that avoids memory allocation.
// We do this using `snprintf` with a limited buffer. // We do this using `snprintf` with a limited buffer.
static void mi_vfprintf( FILE* out, const char* prefix, const char* fmt, va_list args ) { static void mi_vfprintf( FILE* out, const char* prefix, const char* fmt, va_list args ) {
char buf[256]; char buf[256];
if (fmt==NULL) return; if (fmt==NULL) return;
if (_mi_preloading() || recurse) return;
recurse = true;
if (out==NULL) out = stdout; if (out==NULL) out = stdout;
vsnprintf(buf,sizeof(buf)-1,fmt,args); vsnprintf(buf,sizeof(buf)-1,fmt,args);
if (prefix != NULL) fputs(prefix,out); #ifdef _WIN32
fputs(buf,out); // on windows with redirection, the C runtime cannot handle locale dependent output
// after the main thread closes so use direct console output.
if (out==stderr) {
if (prefix != NULL) _cputs(prefix);
_cputs(buf);
}
else
#endif
{
if (prefix != NULL) fputs(prefix,out);
fputs(buf,out);
}
recurse = false;
return;
} }
void _mi_fprintf( FILE* out, const char* fmt, ... ) { void _mi_fprintf( FILE* out, const char* fmt, ... ) {
@ -139,6 +166,7 @@ void _mi_verbose_message(const char* fmt, ...) {
void _mi_error_message(const char* fmt, ...) { void _mi_error_message(const char* fmt, ...) {
if (!mi_option_is_enabled(mi_option_show_errors) && !mi_option_is_enabled(mi_option_verbose)) return; if (!mi_option_is_enabled(mi_option_show_errors) && !mi_option_is_enabled(mi_option_verbose)) return;
if (mi_atomic_increment(&error_count) > MAX_ERROR_COUNT) return;
va_list args; va_list args;
va_start(args,fmt); va_start(args,fmt);
mi_vfprintf(stderr, "mimalloc: error: ", fmt, args); mi_vfprintf(stderr, "mimalloc: error: ", fmt, args);
@ -148,6 +176,7 @@ void _mi_error_message(const char* fmt, ...) {
void _mi_warning_message(const char* fmt, ...) { 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_option_is_enabled(mi_option_show_errors) && !mi_option_is_enabled(mi_option_verbose)) return;
if (mi_atomic_increment(&error_count) > MAX_ERROR_COUNT) return;
va_list args; va_list args;
va_start(args,fmt); va_start(args,fmt);
mi_vfprintf(stderr, "mimalloc: warning: ", fmt, args); mi_vfprintf(stderr, "mimalloc: warning: ", fmt, args);
@ -179,28 +208,64 @@ static void mi_strlcat(char* dest, const char* src, size_t dest_size) {
dest[dest_size - 1] = 0; dest[dest_size - 1] = 0;
} }
static void mi_option_init(mi_option_desc_t* desc) { #if defined _WIN32
desc->init = DEFAULTED; // On Windows use GetEnvironmentVariable instead of getenv to work
// Read option value from the environment // reliably even when this is invoked before the C runtime is initialized.
char buf[32]; // i.e. when `_mi_preloading() == true`.
mi_strlcpy(buf, "mimalloc_", sizeof(buf)); #include <windows.h>
mi_strlcat(buf, desc->name, sizeof(buf)); static bool mi_getenv(const char* name, char* result, size_t result_size) {
#pragma warning(suppress:4996) result[0] = 0;
char* s = getenv(buf); bool ok = (GetEnvironmentVariableA(name, result, (DWORD)result_size) > 0);
if (s == NULL) { if (!ok) {
size_t buf_size = strlen(buf); char buf[64+1];
for (size_t i = 0; i < buf_size; i++) { size_t len = strlen(name);
buf[i] = toupper(buf[i]); if (len >= sizeof(buf)) len = sizeof(buf) - 1;
for (size_t i = 0; i < len; i++) {
buf[i] = toupper(name[i]);
} }
buf[len] = 0;
ok = (GetEnvironmentVariableA(name, result, (DWORD)result_size) > 0);
}
return ok;
}
#else
static bool mi_getenv(const char* name, char* result, size_t result_size) {
#pragma warning(suppress:4996)
const char* s = getenv(name);
if (s == NULL) {
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;
#pragma warning(suppress:4996) #pragma warning(suppress:4996)
s = getenv(buf); s = getenv(buf);
} }
if (s != NULL) { if (s != NULL && strlen(s) < result_size) {
mi_strlcpy(buf, s, sizeof(buf)); mi_strlcpy(result, s, result_size);
size_t buf_size = strlen(buf); // TODO: use strnlen? return true;
for (size_t i = 0; i < buf_size; i++) { }
buf[i] = toupper(buf[i]); else {
return false;
}
}
#endif
static void mi_option_init(mi_option_desc_t* desc) {
desc->init = DEFAULTED;
// 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;
for (size_t i = 0; i < len; i++) {
buf[i] = toupper(s[i]);
} }
buf[len] = 0;
if (buf[0]==0 || strstr("1;TRUE;YES;ON", buf) != NULL) { if (buf[0]==0 || strstr("1;TRUE;YES;ON", buf) != NULL) {
desc->value = 1; desc->value = 1;
desc->init = INITIALIZED; desc->init = INITIALIZED;

View file

@ -10,8 +10,9 @@ terms of the MIT license. A copy of the license can be found in the file
#include "mimalloc.h" #include "mimalloc.h"
#include "mimalloc-internal.h" #include "mimalloc-internal.h"
#include "mimalloc-atomic.h"
#include <string.h> // memset #include <string.h> // strerror
#include <errno.h> #include <errno.h>
#if defined(_WIN32) #if defined(_WIN32)
@ -33,13 +34,6 @@ terms of the MIT license. A copy of the license can be found in the file
----------------------------------------------------------- */ ----------------------------------------------------------- */
bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats); bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats);
uintptr_t _mi_align_up(uintptr_t sz, size_t alignment) {
uintptr_t x = (sz / alignment) * alignment;
if (x < sz) x += alignment;
if (x < sz) return 0; // overflow
return x;
}
static void* mi_align_up_ptr(void* p, size_t alignment) { static void* mi_align_up_ptr(void* p, size_t alignment) {
return (void*)_mi_align_up((uintptr_t)p, alignment); return (void*)_mi_align_up((uintptr_t)p, alignment);
} }
@ -205,20 +199,21 @@ static void* mi_win_virtual_allocx(void* addr, size_t size, size_t try_alignment
} }
static void* mi_win_virtual_alloc(void* addr, size_t size, size_t try_alignment, DWORD flags) { static void* mi_win_virtual_alloc(void* addr, size_t size, size_t try_alignment, DWORD flags) {
static size_t large_page_try_ok = 0; static volatile uintptr_t large_page_try_ok = 0;
void* p = NULL; void* p = NULL;
if (use_large_os_page(size, try_alignment)) { if (use_large_os_page(size, try_alignment)) {
if (large_page_try_ok > 0) { uintptr_t try_ok = mi_atomic_read(&large_page_try_ok);
if (try_ok > 0) {
// if a large page allocation fails, it seems the calls to VirtualAlloc get very expensive. // 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. // therefore, once a large page allocation failed, we don't try again for `large_page_try_ok` times.
large_page_try_ok--; mi_atomic_compare_exchange(&large_page_try_ok, try_ok - 1, try_ok);
} }
else { else {
// large OS pages must always reserve and commit. // large OS pages must always reserve and commit.
p = mi_win_virtual_allocx(addr, size, try_alignment, MEM_LARGE_PAGES | MEM_COMMIT | MEM_RESERVE | flags); p = mi_win_virtual_allocx(addr, size, try_alignment, MEM_LARGE_PAGES | MEM_COMMIT | MEM_RESERVE | flags);
// fall back to non-large page allocation on error (`p == NULL`). // fall back to non-large page allocation on error (`p == NULL`).
if (p == NULL) { if (p == NULL) {
large_page_try_ok = 10; // on error, don't try again for the next N allocations mi_atomic_write(&large_page_try_ok,10); // on error, don't try again for the next N allocations
} }
} }
} }
@ -242,13 +237,30 @@ static void* mi_wasm_heap_grow(size_t size, size_t try_alignment) {
return (void*) aligned_base; return (void*) aligned_base;
} }
#else #else
static void* mi_unix_mmapx(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 a special area for 4MiB aligned allocations
static volatile intptr_t aligned_base = ((intptr_t)1 << 42); // starting at 4TiB
if (try_alignment <= MI_SEGMENT_SIZE && (size%MI_SEGMENT_SIZE)==0 && (aligned_base%try_alignment)==0) {
intptr_t hint = mi_atomic_add(&aligned_base,size) - size;
p = mmap((void*)hint,size,protect_flags,flags,fd,0);
if (p==MAP_FAILED) p = NULL; // fall back to regular mmap
}
#endif
if (p==NULL) {
p = mmap(NULL,size,protect_flags,flags,fd,0);
}
return p;
}
static void* mi_unix_mmap(size_t size, size_t try_alignment, int protect_flags) { static void* mi_unix_mmap(size_t size, size_t try_alignment, int protect_flags) {
void* p = NULL; void* p = NULL;
#if !defined(MAP_ANONYMOUS) #if !defined(MAP_ANONYMOUS)
#define MAP_ANONYMOUS MAP_ANON #define MAP_ANONYMOUS MAP_ANON
#endif #endif
int flags = MAP_PRIVATE | MAP_ANONYMOUS; int flags = MAP_PRIVATE | MAP_ANONYMOUS;
int gfd = -1; int fd = -1;
#if defined(MAP_ALIGNED) // BSD #if defined(MAP_ALIGNED) // BSD
if (try_alignment > 0) { if (try_alignment > 0) {
size_t n = _mi_bsr(try_alignment); size_t n = _mi_bsr(try_alignment);
@ -261,14 +273,12 @@ static void* mi_unix_mmap(size_t size, size_t try_alignment, int protect_flags)
protect_flags |= PROT_MAX(PROT_READ | PROT_WRITE); // BSD protect_flags |= PROT_MAX(PROT_READ | PROT_WRITE); // BSD
#endif #endif
#if defined(VM_MAKE_TAG) #if defined(VM_MAKE_TAG)
// tracking anonymous page with a specific ID // darwin: tracking anonymous page with a specific ID all up to 98 are taken officially but LLVM sanitizers had taken 99
// all up to 98 are taken officially but LLVM fd = VM_MAKE_TAG(100);
// sanitizers had taken 99
gfd = VM_MAKE_TAG(100);
#endif #endif
if (large_os_page_size > 0 && use_large_os_page(size, try_alignment)) { if (large_os_page_size > 0 && use_large_os_page(size, try_alignment)) {
int lflags = flags; int lflags = flags;
int fd = -1; int lfd = fd;
#ifdef MAP_ALIGNED_SUPER #ifdef MAP_ALIGNED_SUPER
lflags |= MAP_ALIGNED_SUPER; lflags |= MAP_ALIGNED_SUPER;
#endif #endif
@ -279,18 +289,18 @@ static void* mi_unix_mmap(size_t size, size_t try_alignment, int protect_flags)
lflags |= MAP_HUGE_2MB; lflags |= MAP_HUGE_2MB;
#endif #endif
#ifdef VM_FLAGS_SUPERPAGE_SIZE_2MB #ifdef VM_FLAGS_SUPERPAGE_SIZE_2MB
fd = VM_FLAGS_SUPERPAGE_SIZE_2MB | gfd; lfd |= VM_FLAGS_SUPERPAGE_SIZE_2MB;
#endif #endif
if (lflags != flags) { if (lflags != flags) {
// try large page allocation // try large page allocation
// TODO: if always failing due to permissions or no huge pages, try to avoid repeatedly trying? // TODO: if always failing due to permissions or no huge pages, try to avoid repeatedly trying?
// Should we check this in _mi_os_init? (as on Windows) // Should we check this in _mi_os_init? (as on Windows)
p = mmap(NULL, size, protect_flags, lflags, fd, 0); p = mi_unix_mmapx(size, try_alignment, protect_flags, lflags, lfd);
if (p == MAP_FAILED) p = NULL; // fall back to regular mmap if large is exhausted or no permission if (p == MAP_FAILED) p = NULL; // fall back to regular mmap if large is exhausted or no permission
} }
} }
if (p == NULL) { if (p == NULL) {
p = mmap(NULL, size, protect_flags, flags, gfd, 0); p = mi_unix_mmapx(size, try_alignment, protect_flags, flags, fd);
if (p == MAP_FAILED) p = NULL; if (p == MAP_FAILED) p = NULL;
} }
return p; return p;
@ -446,7 +456,7 @@ 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); return mi_os_page_align_areax(true, addr, size, newsize);
} }
// Commit/Decommit memory. // Commit/Decommit memory.
// Usuelly commit is aligned liberal, while decommit is aligned conservative. // Usuelly commit is aligned liberal, while decommit is aligned conservative.
// (but not for the reset version where we want commit to be conservative as well) // (but not for the reset version where we want commit to be conservative as well)
static bool mi_os_commitx(void* addr, size_t size, bool commit, bool conservative, mi_stats_t* stats) { static bool mi_os_commitx(void* addr, size_t size, bool commit, bool conservative, mi_stats_t* stats) {
@ -478,7 +488,7 @@ static bool mi_os_commitx(void* addr, size_t size, bool commit, bool conservativ
err = mprotect(start, csize, (commit ? (PROT_READ | PROT_WRITE) : PROT_NONE)); err = mprotect(start, csize, (commit ? (PROT_READ | PROT_WRITE) : PROT_NONE));
#endif #endif
if (err != 0) { if (err != 0) {
_mi_warning_message("commit/decommit error: start: 0x%8p, csize: 0x%8zux, err: %i\n", start, csize, err); _mi_warning_message("commit/decommit error: start: 0x%p, csize: 0x%x, err: %i\n", start, csize, err);
} }
mi_assert_internal(err == 0); mi_assert_internal(err == 0);
return (err == 0); return (err == 0);
@ -510,8 +520,10 @@ static bool mi_os_resetx(void* addr, size_t size, bool reset, mi_stats_t* stats)
else _mi_stat_decrease(&stats->reset, csize); else _mi_stat_decrease(&stats->reset, csize);
if (!reset) return true; // nothing to do on unreset! if (!reset) return true; // nothing to do on unreset!
#if MI_DEBUG>1 #if (MI_DEBUG>1)
memset(start, 0, csize); // pretend it is eagerly reset if (!mi_option_is_enabled(mi_option_secure)) {
memset(start, 0, csize); // pretend it is eagerly reset
}
#endif #endif
#if defined(_WIN32) #if defined(_WIN32)
@ -526,7 +538,7 @@ static bool mi_os_resetx(void* addr, size_t size, bool reset, mi_stats_t* stats)
void* p = VirtualAlloc(start, csize, MEM_RESET, PAGE_READWRITE); void* p = VirtualAlloc(start, csize, MEM_RESET, PAGE_READWRITE);
mi_assert_internal(p == start); mi_assert_internal(p == start);
if (p != start) return false; if (p != start) return false;
} }
#else #else
#if defined(MADV_FREE) #if defined(MADV_FREE)
static int advice = MADV_FREE; static int advice = MADV_FREE;
@ -542,7 +554,7 @@ static bool mi_os_resetx(void* addr, size_t size, bool reset, mi_stats_t* stats)
int err = madvise(start, csize, MADV_DONTNEED); int err = madvise(start, csize, MADV_DONTNEED);
#endif #endif
if (err != 0) { if (err != 0) {
_mi_warning_message("madvise reset error: start: 0x%8p, csize: 0x%8zux, errno: %i\n", start, csize, errno); _mi_warning_message("madvise reset error: start: 0x%p, csize: 0x%x, errno: %i\n", start, csize, errno);
} }
//mi_assert(err == 0); //mi_assert(err == 0);
if (err != 0) return false; if (err != 0) return false;
@ -591,7 +603,7 @@ static bool mi_os_protectx(void* addr, size_t size, bool protect) {
err = mprotect(start, csize, protect ? PROT_NONE : (PROT_READ | PROT_WRITE)); err = mprotect(start, csize, protect ? PROT_NONE : (PROT_READ | PROT_WRITE));
#endif #endif
if (err != 0) { if (err != 0) {
_mi_warning_message("mprotect error: start: 0x%8p, csize: 0x%8zux, err: %i\n", start, csize, err); _mi_warning_message("mprotect error: start: 0x%p, csize: 0x%x, err: %i\n", start, csize, err);
} }
return (err == 0); return (err == 0);
} }

View file

@ -97,7 +97,7 @@ uint8_t _mi_bsr(uintptr_t x) {
// Returns MI_BIN_HUGE if the size is too large. // Returns MI_BIN_HUGE if the size is too large.
// We use `wsize` for the size in "machine word sizes", // We use `wsize` for the size in "machine word sizes",
// i.e. byte size == `wsize*sizeof(void*)`. // i.e. byte size == `wsize*sizeof(void*)`.
inline uint8_t _mi_bin(size_t size) { extern inline uint8_t _mi_bin(size_t size) {
size_t wsize = _mi_wsize_from_size(size); size_t wsize = _mi_wsize_from_size(size);
uint8_t bin; uint8_t bin;
if (wsize <= 1) { if (wsize <= 1) {
@ -120,13 +120,13 @@ inline uint8_t _mi_bin(size_t size) {
bin = MI_BIN_HUGE; bin = MI_BIN_HUGE;
} }
else { else {
#if defined(MI_ALIGN4W) #if defined(MI_ALIGN4W)
if (wsize <= 16) { wsize = (wsize+3)&~3; } // round to 4x word sizes if (wsize <= 16) { wsize = (wsize+3)&~3; } // round to 4x word sizes
#endif #endif
wsize--; wsize--;
// find the highest bit // find the highest bit
uint8_t b = mi_bsr32((uint32_t)wsize); uint8_t b = mi_bsr32((uint32_t)wsize);
// and use the top 3 bits to determine the bin (~16% worst internal fragmentation). // 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 // - adjust with 3 because we use do not round the first 8 sizes
// which each get an exact bin // which each get an exact bin
bin = ((b << 2) + (uint8_t)((wsize >> (b - 2)) & 0x03)) - 3; bin = ((b << 2) + (uint8_t)((wsize >> (b - 2)) & 0x03)) - 3;

View file

@ -15,8 +15,6 @@ terms of the MIT license. A copy of the license can be found in the file
#include "mimalloc-internal.h" #include "mimalloc-internal.h"
#include "mimalloc-atomic.h" #include "mimalloc-atomic.h"
#include <string.h> // memset, memcpy
/* ----------------------------------------------------------- /* -----------------------------------------------------------
Definition of page queues for each block size Definition of page queues for each block size
----------------------------------------------------------- */ ----------------------------------------------------------- */
@ -73,10 +71,11 @@ static bool mi_page_is_valid_init(mi_page_t* page) {
mi_assert_internal(page->block_size > 0); mi_assert_internal(page->block_size > 0);
mi_assert_internal(page->used <= page->capacity); mi_assert_internal(page->used <= page->capacity);
mi_assert_internal(page->capacity <= page->reserved); mi_assert_internal(page->capacity <= page->reserved);
mi_segment_t* segment = _mi_page_segment(page); mi_segment_t* segment = _mi_page_segment(page);
uint8_t* start = _mi_page_start(segment,page,NULL); uint8_t* start = _mi_page_start(segment,page,NULL);
mi_assert_internal(start == _mi_segment_page_start(segment,page,page->block_size,NULL)); mi_assert_internal(start == _mi_segment_page_start(segment,page,page->block_size,NULL));
mi_assert_internal(segment->thread_id==0 || segment->thread_id == mi_page_thread_id(page));
//mi_assert_internal(start + page->capacity*page->block_size == page->top); //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->free));
@ -95,7 +94,9 @@ static bool mi_page_is_valid_init(mi_page_t* page) {
bool _mi_page_is_valid(mi_page_t* page) { bool _mi_page_is_valid(mi_page_t* page) {
mi_assert_internal(mi_page_is_valid_init(page)); mi_assert_internal(mi_page_is_valid_init(page));
#if MI_SECURE
mi_assert_internal(page->cookie != 0); mi_assert_internal(page->cookie != 0);
#endif
if (page->heap!=NULL) { if (page->heap!=NULL) {
mi_segment_t* segment = _mi_page_segment(page); mi_segment_t* segment = _mi_page_segment(page);
mi_assert_internal(!_mi_process_is_initialized || segment->thread_id == page->heap->thread_id); mi_assert_internal(!_mi_process_is_initialized || segment->thread_id == page->heap->thread_id);
@ -121,7 +122,7 @@ void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay ) {
else if (mi_unlikely(mi_tf_delayed(tfree) == MI_DELAYED_FREEING)) { else if (mi_unlikely(mi_tf_delayed(tfree) == MI_DELAYED_FREEING)) {
mi_atomic_yield(); // delay until outstanding MI_DELAYED_FREEING are done. mi_atomic_yield(); // delay until outstanding MI_DELAYED_FREEING are done.
continue; // and try again continue; // and try again
} }
} }
while((mi_tf_delayed(tfreex) != mi_tf_delayed(tfree)) && // avoid atomic operation if already equal while((mi_tf_delayed(tfreex) != mi_tf_delayed(tfree)) && // avoid atomic operation if already equal
!mi_atomic_compare_exchange((volatile uintptr_t*)&page->thread_free, tfreex, tfree)); !mi_atomic_compare_exchange((volatile uintptr_t*)&page->thread_free, tfreex, tfree));
@ -216,7 +217,7 @@ static mi_page_t* mi_page_fresh_alloc(mi_heap_t* heap, mi_page_queue_t* pq, size
mi_page_t* page = _mi_segment_page_alloc(block_size, &heap->tld->segments, &heap->tld->os); mi_page_t* page = _mi_segment_page_alloc(block_size, &heap->tld->segments, &heap->tld->os);
if (page == NULL) return NULL; if (page == NULL) return NULL;
mi_page_init(heap, page, block_size, &heap->tld->stats); mi_page_init(heap, page, block_size, &heap->tld->stats);
mi_heap_stat_increase( heap, pages, 1); _mi_stat_increase( &heap->tld->stats.pages, 1);
mi_page_queue_push(heap, pq, page); mi_page_queue_push(heap, pq, page);
mi_assert_expensive(_mi_page_is_valid(page)); mi_assert_expensive(_mi_page_is_valid(page));
return page; return page;
@ -260,7 +261,7 @@ void _mi_heap_delayed_free(mi_heap_t* heap) {
mi_block_t* next = mi_block_nextx(heap->cookie,block); mi_block_t* next = mi_block_nextx(heap->cookie,block);
// use internal free instead of regular one to keep stats etc correct // use internal free instead of regular one to keep stats etc correct
if (!_mi_free_delayed_block(block)) { if (!_mi_free_delayed_block(block)) {
// we might already start delayed freeing while another thread has not yet // 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.
mi_block_t* dfree; mi_block_t* dfree;
do { do {
@ -352,7 +353,7 @@ void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force) {
// account for huge pages here // account for huge pages here
if (page->block_size > MI_LARGE_SIZE_MAX) { if (page->block_size > MI_LARGE_SIZE_MAX) {
mi_heap_stat_decrease(page->heap, huge, page->block_size); _mi_stat_decrease(&page->heap->tld->stats.huge, page->block_size);
} }
// remove from the page list // remove from the page list
@ -384,8 +385,9 @@ void _mi_page_retire(mi_page_t* page) {
// is the only page left with free blocks. It is not clear // is the only page left with free blocks. It is not clear
// how to check this efficiently though... for now we just check // how to check this efficiently though... for now we just check
// if its neighbours are almost fully used. // if its neighbours are almost fully used.
if (mi_likely(page->block_size <= MI_SMALL_SIZE_MAX)) { if (mi_likely(page->block_size <= MI_MEDIUM_SIZE_MAX)) {
if (mi_page_mostly_used(page->prev) && mi_page_mostly_used(page->next)) { if (mi_page_mostly_used(page->prev) && mi_page_mostly_used(page->next)) {
_mi_stat_counter_increase(&_mi_stats_main.page_no_retire,1);
return; // dont't retire after all return; // dont't retire after all
} }
} }
@ -404,7 +406,60 @@ void _mi_page_retire(mi_page_t* page) {
#define MI_MAX_SLICES (1UL << MI_MAX_SLICE_SHIFT) #define MI_MAX_SLICES (1UL << MI_MAX_SLICE_SHIFT)
#define MI_MIN_SLICES (2) #define MI_MIN_SLICES (2)
static void mi_page_free_list_extend( mi_heap_t* heap, mi_page_t* page, size_t extend, mi_stats_t* stats) static void mi_page_free_list_extend_secure(mi_heap_t* heap, mi_page_t* page, size_t extend, mi_stats_t* stats) {
UNUSED(stats);
mi_assert_internal(page->free == NULL);
mi_assert_internal(page->local_free == NULL);
mi_assert_internal(page->capacity + extend <= page->reserved);
void* page_area = _mi_page_start(_mi_page_segment(page), page, NULL);
size_t bsize = page->block_size;
// initialize a randomized free list
// set up `slice_count` slices to alternate between
size_t shift = MI_MAX_SLICE_SHIFT;
while ((extend >> shift) == 0) {
shift--;
}
size_t slice_count = (size_t)1U << shift;
size_t slice_extend = extend / slice_count;
mi_assert_internal(slice_extend >= 1);
mi_block_t* blocks[MI_MAX_SLICES]; // current start of the slice
size_t counts[MI_MAX_SLICES]; // available objects in the slice
for (size_t i = 0; i < slice_count; i++) {
blocks[i] = mi_page_block_at(page, page_area, page->capacity + i*slice_extend);
counts[i] = slice_extend;
}
counts[slice_count-1] += (extend % slice_count); // final slice holds the modulus too (todo: distribute evenly?)
// and initialize the free list by randomly threading through them
// set up first element
size_t current = _mi_heap_random(heap) % slice_count;
counts[current]--;
page->free = blocks[current];
// and iterate through the rest
uintptr_t rnd = heap->random;
for (size_t i = 1; i < extend; i++) {
// call random_shuffle only every INTPTR_SIZE rounds
size_t round = i%MI_INTPTR_SIZE;
if (round == 0) rnd = _mi_random_shuffle(rnd);
// select a random next slice index
size_t next = ((rnd >> 8*round) & (slice_count-1));
while (counts[next]==0) { // ensure it still has space
next++;
if (next==slice_count) next = 0;
}
// and link the current block to it
counts[next]--;
mi_block_t* block = blocks[current];
blocks[current] = (mi_block_t*)((uint8_t*)block + bsize); // bump to the following block
mi_block_set_next(page, block, blocks[next]); // and set next; note: we may have `current == next`
current = next;
}
mi_block_set_next(page, blocks[current], NULL); // end of the list
heap->random = _mi_random_shuffle(rnd);
}
static void mi_page_free_list_extend( mi_page_t* page, size_t extend, mi_stats_t* stats)
{ {
UNUSED(stats); UNUSED(stats);
mi_assert_internal(page->free == NULL); mi_assert_internal(page->free == NULL);
@ -413,66 +468,17 @@ static void mi_page_free_list_extend( mi_heap_t* heap, mi_page_t* page, size_t e
void* page_area = _mi_page_start(_mi_page_segment(page), page, NULL ); void* page_area = _mi_page_start(_mi_page_segment(page), page, NULL );
size_t bsize = page->block_size; size_t bsize = page->block_size;
mi_block_t* start = mi_page_block_at(page, page_area, page->capacity); mi_block_t* start = mi_page_block_at(page, page_area, page->capacity);
if (extend < MI_MIN_SLICES || !mi_option_is_enabled(mi_option_secure)) {
// initialize a sequential free list
mi_block_t* end = mi_page_block_at(page, page_area, page->capacity + extend - 1);
mi_block_t* block = start;
for (size_t i = 0; i < extend; i++) {
mi_block_t* next = (mi_block_t*)((uint8_t*)block + bsize);
mi_block_set_next(page,block,next);
block = next;
}
mi_block_set_next(page, end, NULL);
page->free = start;
}
else {
// initialize a randomized free list
// set up `slice_count` slices to alternate between
size_t shift = MI_MAX_SLICE_SHIFT;
while ((extend >> shift) == 0) {
shift--;
}
size_t slice_count = (size_t)1U << shift;
size_t slice_extend = extend / slice_count;
mi_assert_internal(slice_extend >= 1);
mi_block_t* blocks[MI_MAX_SLICES]; // current start of the slice
size_t counts[MI_MAX_SLICES]; // available objects in the slice
for (size_t i = 0; i < slice_count; i++) {
blocks[i] = mi_page_block_at(page, page_area, page->capacity + i*slice_extend);
counts[i] = slice_extend;
}
counts[slice_count-1] += (extend % slice_count); // final slice holds the modulus too (todo: distribute evenly?)
// and initialize the free list by randomly threading through them // initialize a sequential free list
// set up first element mi_block_t* last = mi_page_block_at(page, page_area, page->capacity + extend - 1);
size_t current = _mi_heap_random(heap) % slice_count; mi_block_t* block = start;
counts[current]--; while(block <= last) {
page->free = blocks[current]; mi_block_t* next = (mi_block_t*)((uint8_t*)block + bsize);
// and iterate through the rest mi_block_set_next(page,block,next);
uintptr_t rnd = heap->random; block = next;
for (size_t i = 1; i < extend; i++) {
// call random_shuffle only every INTPTR_SIZE rounds
size_t round = i%MI_INTPTR_SIZE;
if (round == 0) rnd = _mi_random_shuffle(rnd);
// select a random next slice index
size_t next = ((rnd >> 8*round) & (slice_count-1));
while (counts[next]==0) { // ensure it still has space
next++;
if (next==slice_count) next = 0;
}
// and link the current block to it
counts[next]--;
mi_block_t* block = blocks[current];
blocks[current] = (mi_block_t*)((uint8_t*)block + bsize); // bump to the following block
mi_block_set_next(page, block, blocks[next]); // and set next; note: we may have `current == next`
current = next;
}
mi_block_set_next( page, blocks[current], NULL); // end of the list
heap->random = _mi_random_shuffle(rnd);
} }
// enable the new free list mi_block_set_next(page, last, NULL);
page->capacity += (uint16_t)extend; page->free = start;
_mi_stat_increase(&stats->page_committed, extend * page->block_size);
} }
/* ----------------------------------------------------------- /* -----------------------------------------------------------
@ -500,7 +506,7 @@ static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_stats_t* st
if (page->capacity >= page->reserved) return; if (page->capacity >= page->reserved) return;
size_t page_size; size_t page_size;
_mi_page_start(_mi_page_segment(page), page, &page_size); _mi_page_start(_mi_page_segment(page), page, &page_size);
_mi_stat_increase(&stats->pages_extended, 1); _mi_stat_increase(&stats->pages_extended, 1);
// calculate the extend count // calculate the extend count
@ -518,7 +524,15 @@ static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_stats_t* st
mi_assert_internal(extend < (1UL<<16)); mi_assert_internal(extend < (1UL<<16));
// and append the extend the free list // and append the extend the free list
mi_page_free_list_extend(heap, page, extend, stats ); if (extend < MI_MIN_SLICES || !mi_option_is_enabled(mi_option_secure)) {
mi_page_free_list_extend(page, extend, stats );
}
else {
mi_page_free_list_extend_secure(heap, page, extend, stats);
}
// enable the new free list
page->capacity += (uint16_t)extend;
_mi_stat_increase(&stats->page_committed, extend * page->block_size);
mi_assert_expensive(mi_page_is_valid_init(page)); mi_assert_expensive(mi_page_is_valid_init(page));
} }
@ -535,7 +549,9 @@ static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t block_size, mi
page->block_size = block_size; page->block_size = block_size;
mi_assert_internal(page_size / block_size < (1L<<16)); mi_assert_internal(page_size / block_size < (1L<<16));
page->reserved = (uint16_t)(page_size / block_size); page->reserved = (uint16_t)(page_size / block_size);
#if MI_SECURE
page->cookie = _mi_heap_random(heap) | 1; page->cookie = _mi_heap_random(heap) | 1;
#endif
mi_assert_internal(page->capacity == 0); mi_assert_internal(page->capacity == 0);
mi_assert_internal(page->free == NULL); mi_assert_internal(page->free == NULL);
@ -545,7 +561,9 @@ static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t block_size, mi
mi_assert_internal(page->next == NULL); mi_assert_internal(page->next == NULL);
mi_assert_internal(page->prev == NULL); mi_assert_internal(page->prev == NULL);
mi_assert_internal(page->flags.has_aligned == false); mi_assert_internal(page->flags.has_aligned == false);
#if MI_SECURE
mi_assert_internal(page->cookie != 0); mi_assert_internal(page->cookie != 0);
#endif
mi_assert_expensive(mi_page_is_valid_init(page)); mi_assert_expensive(mi_page_is_valid_init(page));
// initialize an initial free list // initialize an initial free list
@ -684,8 +702,8 @@ static mi_page_t* mi_huge_page_alloc(mi_heap_t* heap, size_t size) {
if (page != NULL) { if (page != NULL) {
mi_assert_internal(mi_page_immediate_available(page)); mi_assert_internal(mi_page_immediate_available(page));
mi_assert_internal(page->block_size == block_size); mi_assert_internal(page->block_size == block_size);
mi_heap_stat_increase( heap, huge, block_size); _mi_stat_increase( &heap->tld->stats.huge, block_size);
} }
return page; return page;
} }
@ -704,10 +722,10 @@ void* _mi_malloc_generic(mi_heap_t* heap, size_t size) mi_attr_noexcept
// call potential deferred free routines // call potential deferred free routines
_mi_deferred_free(heap, false); _mi_deferred_free(heap, false);
// free delayed frees from other threads // free delayed frees from other threads
_mi_heap_delayed_free(heap); _mi_heap_delayed_free(heap);
// huge allocation? // huge allocation?
mi_page_t* page; mi_page_t* page;
if (mi_unlikely(size > MI_LARGE_SIZE_MAX)) { if (mi_unlikely(size > MI_LARGE_SIZE_MAX)) {
@ -729,11 +747,4 @@ void* _mi_malloc_generic(mi_heap_t* heap, size_t size) mi_attr_noexcept
// and try again, this time succeeding! (i.e. this should never recurse) // and try again, this time succeeding! (i.e. this should never recurse)
return _mi_page_malloc(heap, page, size); return _mi_page_malloc(heap, page, size);
/*
if (page->used == page->reserved) {
// needed for huge pages to free reliably from other threads.
mi_page_to_full(page,mi_page_queue_of(page));
}
return p;
*/
} }

View file

@ -226,6 +226,7 @@ static void mi_segments_track_size(long segment_size, mi_segments_tld_t* tld) {
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, size_t segment_size, mi_segments_tld_t* tld) {
segment->thread_id = 0;
mi_segments_track_size(-((long)segment_size),tld); mi_segments_track_size(-((long)segment_size),tld);
if (mi_option_is_enabled(mi_option_secure)) { if (mi_option_is_enabled(mi_option_secure)) {
_mi_mem_unprotect(segment, segment->segment_size); // ensure no more guard pages are set _mi_mem_unprotect(segment, segment->segment_size); // ensure no more guard pages are set
@ -248,17 +249,19 @@ static mi_segment_t* mi_segment_cache_pop(size_t segment_size, mi_segments_tld_t
tld->cache = segment->next; tld->cache = segment->next;
segment->next = NULL; segment->next = NULL;
mi_assert_internal(segment->segment_size == MI_SEGMENT_SIZE); mi_assert_internal(segment->segment_size == MI_SEGMENT_SIZE);
_mi_stat_decrease(&tld->stats->segments_cache, 1);
return segment; return segment;
} }
static bool mi_segment_cache_full(mi_segments_tld_t* tld) { static bool mi_segment_cache_full(mi_segments_tld_t* tld) {
if (tld->cache_count < MI_SEGMENT_CACHE_MAX && if (tld->cache_count < MI_SEGMENT_CACHE_MAX
tld->cache_count < (1 + (tld->peak_count / MI_SEGMENT_CACHE_FRACTION))) { // always allow 1 element cache && tld->cache_count < (1 + (tld->peak_count / MI_SEGMENT_CACHE_FRACTION))
) { // always allow 1 element cache
return false; return false;
} }
// take the opportunity to reduce the segment cache if it is too large (now) // 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? // TODO: this never happens as we check against peak usage, should we use current usage instead?
while (tld->cache_count > (1 + (tld->peak_count / MI_SEGMENT_CACHE_FRACTION))) { while (tld->cache_count > MI_SEGMENT_CACHE_MAX ) { //(1 + (tld->peak_count / MI_SEGMENT_CACHE_FRACTION))) {
mi_segment_t* segment = mi_segment_cache_pop(0,tld); mi_segment_t* segment = mi_segment_cache_pop(0,tld);
mi_assert_internal(segment != NULL); mi_assert_internal(segment != NULL);
if (segment != NULL) mi_segment_os_free(segment, segment->segment_size, tld); if (segment != NULL) mi_segment_os_free(segment, segment->segment_size, tld);
@ -269,7 +272,9 @@ static bool mi_segment_cache_full(mi_segments_tld_t* tld) {
static bool mi_segment_cache_push(mi_segment_t* segment, mi_segments_tld_t* tld) { 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(!mi_segment_is_in_free_queue(segment, tld));
mi_assert_internal(segment->next == NULL); mi_assert_internal(segment->next == NULL);
if (segment->segment_size != MI_SEGMENT_SIZE || mi_segment_cache_full(tld)) return false; if (segment->segment_size != MI_SEGMENT_SIZE || mi_segment_cache_full(tld)) {
return false;
}
mi_assert_internal(segment->segment_size == MI_SEGMENT_SIZE); mi_assert_internal(segment->segment_size == MI_SEGMENT_SIZE);
if (mi_option_is_enabled(mi_option_cache_reset)) { if (mi_option_is_enabled(mi_option_cache_reset)) {
_mi_mem_reset((uint8_t*)segment + segment->segment_info_size, segment->segment_size - segment->segment_info_size, tld->stats); _mi_mem_reset((uint8_t*)segment + segment->segment_info_size, segment->segment_size - segment->segment_info_size, tld->stats);
@ -277,6 +282,7 @@ static bool mi_segment_cache_push(mi_segment_t* segment, mi_segments_tld_t* tld)
segment->next = tld->cache; segment->next = tld->cache;
tld->cache = segment; tld->cache = segment;
tld->cache_count++; tld->cache_count++;
_mi_stat_increase(&tld->stats->segments_cache,1);
return true; return true;
} }
@ -318,7 +324,7 @@ static mi_segment_t* mi_segment_alloc(size_t required, mi_page_kind_t page_kind,
size_t page_size = (page_kind == MI_PAGE_HUGE ? segment_size : (size_t)1 << page_shift); size_t page_size = (page_kind == MI_PAGE_HUGE ? segment_size : (size_t)1 << page_shift);
// Try to get it from our thread local cache first // Try to get it from our thread local cache first
bool commit = mi_option_is_enabled(mi_option_eager_commit) || (page_kind > MI_PAGE_MEDIUM); bool commit = mi_option_is_enabled(mi_option_eager_commit) || (page_kind > MI_PAGE_MEDIUM);
bool protection_still_good = false; bool protection_still_good = false;
mi_segment_t* segment = mi_segment_cache_pop(segment_size, tld); mi_segment_t* segment = mi_segment_cache_pop(segment_size, tld);
if (segment != NULL) { if (segment != NULL) {
@ -407,8 +413,7 @@ static void mi_segment_free(mi_segment_t* segment, bool force, mi_segments_tld_t
mi_assert_expensive(!mi_segment_queue_contains(&tld->medium_free, segment)); mi_assert_expensive(!mi_segment_queue_contains(&tld->medium_free, segment));
mi_assert(segment->next == NULL); mi_assert(segment->next == NULL);
mi_assert(segment->prev == NULL); mi_assert(segment->prev == NULL);
_mi_stat_decrease(&tld->stats->page_committed, segment->segment_info_size); _mi_stat_decrease(&tld->stats->page_committed, segment->segment_info_size);
segment->thread_id = 0;
// update reset memory statistics // update reset memory statistics
/* /*
@ -613,6 +618,7 @@ bool _mi_segment_try_reclaim_abandoned( mi_heap_t* heap, bool try_all, mi_segmen
} }
else { else {
// otherwise reclaim it // otherwise reclaim it
mi_page_init_flags(page,segment->thread_id);
_mi_page_reclaim(heap,page); _mi_page_reclaim(heap,page);
} }
} }
@ -643,6 +649,7 @@ static mi_page_t* mi_segment_page_alloc_in(mi_segment_t* segment, mi_segments_tl
mi_assert_internal(mi_segment_has_free(segment)); mi_assert_internal(mi_segment_has_free(segment));
mi_page_t* page = mi_segment_find_free(segment, tld->stats); mi_page_t* page = mi_segment_find_free(segment, tld->stats);
page->segment_in_use = true; page->segment_in_use = true;
mi_page_init_flags(page,segment->thread_id);
segment->used++; segment->used++;
mi_assert_internal(segment->used <= segment->capacity); mi_assert_internal(segment->used <= segment->capacity);
if (segment->used == segment->capacity) { if (segment->used == segment->capacity) {
@ -682,6 +689,7 @@ static mi_page_t* mi_segment_large_page_alloc(mi_segments_tld_t* tld, mi_os_tld_
segment->used = 1; segment->used = 1;
mi_page_t* page = &segment->pages[0]; mi_page_t* page = &segment->pages[0];
page->segment_in_use = true; page->segment_in_use = true;
mi_page_init_flags(page,segment->thread_id);
return page; return page;
} }
@ -693,22 +701,27 @@ static mi_page_t* mi_segment_huge_page_alloc(size_t size, mi_segments_tld_t* tld
segment->used = 1; segment->used = 1;
mi_page_t* page = &segment->pages[0]; mi_page_t* page = &segment->pages[0];
page->segment_in_use = true; page->segment_in_use = true;
mi_page_init_flags(page,segment->thread_id);
return page; return page;
} }
/* ----------------------------------------------------------- /* -----------------------------------------------------------
Page allocation and free Page allocation and free
----------------------------------------------------------- */ ----------------------------------------------------------- */
static bool mi_is_good_fit(size_t bsize, size_t size) {
// good fit if no more than 25% wasted
return (bsize > 0 && size > 0 && bsize < size && (size - (size % bsize)) < (size/4));
}
mi_page_t* _mi_segment_page_alloc(size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { mi_page_t* _mi_segment_page_alloc(size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) {
mi_page_t* page; mi_page_t* page;
if (block_size <= (MI_SMALL_PAGE_SIZE/16)*3) { if (block_size <= MI_SMALL_SIZE_MAX || mi_is_good_fit(block_size,MI_SMALL_PAGE_SIZE)) {
page = mi_segment_small_page_alloc(tld,os_tld); page = mi_segment_small_page_alloc(tld,os_tld);
} }
else if (block_size <= (MI_MEDIUM_PAGE_SIZE/16)*3) { else if (block_size <= MI_MEDIUM_SIZE_MAX || mi_is_good_fit(block_size, MI_MEDIUM_PAGE_SIZE)) {
page = mi_segment_medium_page_alloc(tld, os_tld); page = mi_segment_medium_page_alloc(tld, os_tld);
} }
else if (block_size < (MI_LARGE_SIZE_MAX - sizeof(mi_segment_t))) { else if (block_size < MI_LARGE_SIZE_MAX || mi_is_good_fit(block_size, MI_LARGE_PAGE_SIZE - sizeof(mi_segment_t))) {
page = mi_segment_large_page_alloc(tld, os_tld); page = mi_segment_large_page_alloc(tld, os_tld);
} }
else { else {

View file

@ -99,14 +99,14 @@ static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) {
mi_stat_add(&stats->pages_abandoned, &src->pages_abandoned, 1); mi_stat_add(&stats->pages_abandoned, &src->pages_abandoned, 1);
mi_stat_add(&stats->segments_abandoned, &src->segments_abandoned, 1); mi_stat_add(&stats->segments_abandoned, &src->segments_abandoned, 1);
mi_stat_add(&stats->mmap_calls, &src->mmap_calls, 1); mi_stat_add(&stats->mmap_calls, &src->mmap_calls, 1);
mi_stat_add(&stats->mmap_ensure_aligned, &src->mmap_ensure_aligned, 1);
mi_stat_add(&stats->mmap_right_align, &src->mmap_right_align, 1);
mi_stat_add(&stats->commit_calls, &src->commit_calls, 1); mi_stat_add(&stats->commit_calls, &src->commit_calls, 1);
mi_stat_add(&stats->threads, &src->threads, 1); mi_stat_add(&stats->threads, &src->threads, 1);
mi_stat_add(&stats->pages_extended, &src->pages_extended, 1); mi_stat_add(&stats->pages_extended, &src->pages_extended, 1);
mi_stat_add(&stats->malloc, &src->malloc, 1); mi_stat_add(&stats->malloc, &src->malloc, 1);
mi_stat_add(&stats->segments_cache, &src->segments_cache, 1);
mi_stat_add(&stats->huge, &src->huge, 1); mi_stat_add(&stats->huge, &src->huge, 1);
mi_stat_counter_add(&stats->page_no_retire, &src->page_no_retire, 1);
mi_stat_counter_add(&stats->searches, &src->searches, 1); mi_stat_counter_add(&stats->searches, &src->searches, 1);
#if MI_STAT>1 #if MI_STAT>1
for (size_t i = 0; i <= MI_BIN_HUGE; i++) { for (size_t i = 0; i <= MI_BIN_HUGE; i++) {
@ -172,10 +172,15 @@ static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t
} }
static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, FILE* out ) { static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, FILE* out ) {
double avg = (stat->count == 0 ? 0.0 : (double)stat->total / (double)stat->count); _mi_fprintf(out, "%10s:", msg);
_mi_fprintf(out,"%10s: %7.1f avg\n", msg, avg); mi_print_amount(stat->total, -1, out);
_mi_fprintf(out, "\n");
} }
static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* msg, FILE* out) {
double avg = (stat->count == 0 ? 0.0 : (double)stat->total / (double)stat->count);
_mi_fprintf(out, "%10s: %7.1f avg\n", msg, avg);
}
static void mi_print_header( FILE* out ) { static void mi_print_header( FILE* out ) {
@ -229,15 +234,15 @@ static void _mi_stats_print(mi_stats_t* stats, double secs, FILE* out) mi_attr_n
mi_stat_print(&stats->page_committed, "touched", 1, out); mi_stat_print(&stats->page_committed, "touched", 1, out);
mi_stat_print(&stats->segments, "segments", -1, out); mi_stat_print(&stats->segments, "segments", -1, out);
mi_stat_print(&stats->segments_abandoned, "-abandoned", -1, out); mi_stat_print(&stats->segments_abandoned, "-abandoned", -1, out);
mi_stat_print(&stats->segments_cache, "-cached", -1, out);
mi_stat_print(&stats->pages, "pages", -1, out); mi_stat_print(&stats->pages, "pages", -1, out);
mi_stat_print(&stats->pages_abandoned, "-abandoned", -1, out); mi_stat_print(&stats->pages_abandoned, "-abandoned", -1, out);
mi_stat_print(&stats->pages_extended, "-extended", 0, out); mi_stat_print(&stats->pages_extended, "-extended", 0, out);
mi_stat_counter_print(&stats->page_no_retire, "-noretire", out);
mi_stat_print(&stats->mmap_calls, "mmaps", 0, out); mi_stat_print(&stats->mmap_calls, "mmaps", 0, out);
mi_stat_print(&stats->mmap_right_align, "mmap fast", 0, out);
mi_stat_print(&stats->mmap_ensure_aligned, "mmap slow", 0, out);
mi_stat_print(&stats->commit_calls, "commits", 0, out); mi_stat_print(&stats->commit_calls, "commits", 0, out);
mi_stat_print(&stats->threads, "threads", 0, out); mi_stat_print(&stats->threads, "threads", 0, out);
mi_stat_counter_print(&stats->searches, "searches", out); mi_stat_counter_print_avg(&stats->searches, "searches", out);
if (secs >= 0.0) _mi_fprintf(out, "%10s: %9.3f s\n", "elapsed", secs); if (secs >= 0.0) _mi_fprintf(out, "%10s: %9.3f s\n", "elapsed", secs);

View file

@ -14,24 +14,33 @@ endif()
# Import mimalloc (if installed) # Import mimalloc (if installed)
find_package(mimalloc 1.0 REQUIRED NO_SYSTEM_ENVIRONMENT_PATH) find_package(mimalloc 1.0 REQUIRED NO_SYSTEM_ENVIRONMENT_PATH)
message(STATUS "Found mimalloc installed at: ${MIMALLOC_TARGET_DIR}")
message(STATUS "${MIMALLOC_INCLUDE_DIR}") # overriding with a dynamic library
# Tests
add_executable(dynamic-override main-override.c) add_executable(dynamic-override main-override.c)
target_link_libraries(dynamic-override PUBLIC mimalloc) target_link_libraries(dynamic-override PUBLIC mimalloc)
add_executable(dynamic-override-cxx main-override.cpp) add_executable(dynamic-override-cxx main-override.cpp)
target_link_libraries(dynamic-override-cxx PUBLIC mimalloc) target_link_libraries(dynamic-override-cxx PUBLIC mimalloc)
# with a static library
# 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_TARGET_DIR}/mimalloc.o)
target_include_directories(static-override-obj PUBLIC ${MIMALLOC_TARGET_DIR}/include)
target_link_libraries(static-override-obj PUBLIC pthread)
# overriding with a static library works too if using the `mimalloc-override.h`
# header to redefine malloc/free. (the library already overrides new/delete)
add_executable(static-override-static main-override-static.c)
target_link_libraries(static-override-static PUBLIC mimalloc-static)
# overriding with a static library: this may not work if the library is linked too late
# on the command line after the C runtime library; but we cannot control that well in CMake
add_executable(static-override main-override.c) add_executable(static-override main-override.c)
target_link_libraries(static-override PUBLIC mimalloc-static) target_link_libraries(static-override PUBLIC mimalloc-static)
add_executable(static-override-cxx main-override.cpp) add_executable(static-override-cxx main-override.cpp)
target_link_libraries(static-override-cxx PUBLIC mimalloc-static) target_link_libraries(static-override-cxx PUBLIC mimalloc-static)
# and with a static object file
add_executable(static-override-obj main-override.c ${MIMALLOC_TARGET_DIR}/mimalloc.o)
target_include_directories(static-override-obj PUBLIC ${MIMALLOC_TARGET_DIR}/include)
target_link_libraries(static-override-obj PUBLIC pthread)

View file

@ -0,0 +1,32 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <mimalloc.h>
#include <mimalloc-override.h> // redefines malloc etc.
int main() {
mi_version();
void* p1 = malloc(78);
void* p2 = malloc(24);
free(p1);
p1 = malloc(8);
//char* s = strdup("hello\n");
free(p2);
p2 = malloc(16);
p1 = realloc(p1, 32);
free(p1);
free(p2);
//free(s);
//mi_collect(true);
/* now test if override worked by allocating/freeing across the api's*/
//p1 = mi_malloc(32);
//free(p1);
//p2 = malloc(32);
//mi_free(p2);
mi_stats_print(NULL);
return 0;
}

View file

@ -5,28 +5,26 @@
#include <mimalloc.h> #include <mimalloc.h>
int main() { int main() {
mi_stats_reset(); mi_version(); // ensure mimalloc library is linked
void* p1 = malloc(78); void* p1 = malloc(78);
void* p2 = malloc(24); void* p2 = malloc(24);
free(p1); free(p1);
p1 = malloc(8); p1 = malloc(8);
char* s = strdup("hello\n"); //char* s = strdup("hello\n");
free(p2); free(p2);
p2 = malloc(16); p2 = malloc(16);
p1 = realloc(p1, 32); p1 = realloc(p1, 32);
free(p1); free(p1);
free(p2); free(p2);
free(s); //free(s);
mi_collect(true); //mi_collect(true);
/* now test if override worked by allocating/freeing across the api's*/ /* now test if override worked by allocating/freeing across the api's*/
p1 = mi_malloc(32); //p1 = mi_malloc(32);
free(p1); //free(p1);
p2 = malloc(32); //p2 = malloc(32);
mi_free(p2); //mi_free(p2);
mi_stats_print(NULL); mi_stats_print(NULL);
return 0; return 0;
} }

View file

@ -4,7 +4,6 @@
#include <string.h> #include <string.h>
#include <mimalloc.h> #include <mimalloc.h>
#include <new> #include <new>
static void* p = malloc(8); static void* p = malloc(8);
@ -22,29 +21,25 @@ public:
~Test() { } ~Test() { }
}; };
int main() { int main() {
mi_stats_reset(); mi_stats_reset(); // ignore earlier allocations
atexit(free_p); atexit(free_p);
void* p1 = malloc(78); void* p1 = malloc(78);
void* p2 = malloc(24); void* p2 = mi_malloc_aligned(16,24);
free(p1); free(p1);
p1 = malloc(8); p1 = malloc(8);
char* s = mi_strdup("hello\n"); char* s = mi_strdup("hello\n");
free(p2); mi_free(p2);
p2 = malloc(16); p2 = malloc(16);
p1 = realloc(p1, 32); p1 = realloc(p1, 32);
free(p1); free(p1);
free(p2); free(p2);
free(s); mi_free(s);
Test* t = new Test(42); Test* t = new Test(42);
delete t; delete t;
t = new (std::nothrow) Test(42); t = new (std::nothrow) Test(42);
delete t; delete t;
int err = mi_posix_memalign(&p1,32,60);
if (!err) free(p1);
free(p);
mi_collect(true);
mi_stats_print(NULL); // MIMALLOC_VERBOSE env is set to 2
return 0; return 0;
} }

View file

@ -1,42 +1,71 @@
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------
Copyright (c) 2018,2019 Microsoft Research, Daan Leijen Copyright (c) 2018,2019 Microsoft Research, Daan Leijen
This is free software; you can redistribute it and/or modify it under the 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 terms of the MIT license.
"LICENSE" at the root of this distribution.
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
/* This is a stress test for the allocator, using multiple threads and /* This is a stress test for the allocator, using multiple threads and
transferring objects between threads. This is not a typical workload transferring objects between threads. This is not a typical workload
but uses a random size distribution. Do not use this test as a benchmark! but uses a random linear size distribution. Do not use this test as a benchmark!
Note: pthreads uses mimalloc to allocate stacks and thus not all
memory is freed at the end. (usually the 320 byte chunks).
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include "mimalloc.h" #include <mimalloc.h>
#include "mimalloc-internal.h"
#include "mimalloc-atomic.h" // argument defaults
static int THREADS = 32; // more repeatable if THREADS <= #processors
static int N = 10; // scaling factor
// static int THREADS = 8; // more repeatable if THREADS <= #processors
// static int N = 100; // scaling factor
#define N (10) // scaling factor
#define THREADS (32)
#define TRANSFERS (1000) #define TRANSFERS (1000)
static volatile void* transfer[TRANSFERS]; static volatile void* transfer[TRANSFERS];
#if (MI_INTPTR_SIZE==8) #if (UINTPTR_MAX != UINT32_MAX)
const uintptr_t cookie = 0xbf58476d1ce4e5b9UL; const uintptr_t cookie = 0xbf58476d1ce4e5b9UL;
#else #else
const uintptr_t cookie = 0x1ce4e5b9UL; const uintptr_t cookie = 0x1ce4e5b9UL;
#endif #endif
static void* atomic_exchange_ptr(volatile void** p, void* newval);
static void* alloc_items(size_t items) { typedef uintptr_t* random_t;
if ((rand()%100) == 0) items *= 100; // 1% huge objects;
static uintptr_t pick(random_t r) {
uintptr_t x = *r;
#if (UINTPTR_MAX > UINT32_MAX)
// by Sebastiano Vigna, see: <http://xoshiro.di.unimi.it/splitmix64.c>
x ^= x >> 30;
x *= 0xbf58476d1ce4e5b9UL;
x ^= x >> 27;
x *= 0x94d049bb133111ebUL;
x ^= x >> 31;
#else
// by Chris Wellons, see: <https://nullprogram.com/blog/2018/07/31/>
x ^= x >> 16;
x *= 0x7feb352dUL;
x ^= x >> 15;
x *= 0x846ca68bUL;
x ^= x >> 16;
#endif
*r = x;
return x;
}
static bool chance(size_t perc, random_t r) {
return (pick(r) % 100 <= perc);
}
static void* alloc_items(size_t items, random_t r) {
if (chance(1, r)) items *= 100; // 1% huge objects;
if (items==40) items++; // pthreads uses that size for stack increases if (items==40) items++; // pthreads uses that size for stack increases
uintptr_t* p = mi_mallocn_tp(uintptr_t,items); uintptr_t* p = (uintptr_t*)mi_malloc(items*sizeof(uintptr_t));
if(p == NULL) return NULL;
for (uintptr_t i = 0; i < items; i++) p[i] = (items - i) ^ cookie; for (uintptr_t i = 0; i < items; i++) p[i] = (items - i) ^ cookie;
return p; return p;
} }
@ -47,7 +76,7 @@ static void free_items(void* p) {
uintptr_t items = (q[0] ^ cookie); uintptr_t items = (q[0] ^ cookie);
for (uintptr_t i = 0; i < items; i++) { for (uintptr_t i = 0; i < items; i++) {
if ((q[i]^cookie) != items - i) { if ((q[i]^cookie) != items - i) {
fprintf(stderr,"memory corruption at block %p at %zu\n", p, i); fprintf(stderr, "memory corruption at block %p at %zu\n", p, i);
abort(); abort();
} }
} }
@ -57,43 +86,45 @@ static void free_items(void* p) {
static void stress(intptr_t tid) { static void stress(intptr_t tid) {
//bench_start_thread();
uintptr_t r = tid ^ 42;
const size_t max_item = 128; // in words const size_t max_item = 128; // in words
const size_t max_item_retained = 10*max_item; const size_t max_item_retained = 10*max_item;
size_t allocs = 80*N*(tid%8 + 1); // some threads do more size_t allocs = 25*N*(tid%8 + 1); // some threads do more
size_t retain = allocs/2; size_t retain = allocs/2;
void** data = NULL; void** data = NULL;
size_t data_size = 0; size_t data_size = 0;
size_t data_top = 0; size_t data_top = 0;
void** retained = mi_mallocn_tp(void*,retain); void** retained = (void**)mi_malloc(retain*sizeof(void*));
size_t retain_top = 0; size_t retain_top = 0;
while (allocs>0 || retain>0) { while (allocs>0 || retain>0) {
if (retain == 0 || ((rand()%4 == 0) && allocs > 0)) { if (retain == 0 || (chance(50, &r) && allocs > 0)) {
// 75% alloc // 50%+ alloc
allocs--; allocs--;
if (data_top >= data_size) { if (data_top >= data_size) {
data_size += 100000; data_size += 100000;
data = mi_reallocn_tp(data, void*, data_size); data = (void**)mi_realloc(data, data_size*sizeof(void*));
} }
data[data_top++] = alloc_items((rand() % max_item) + 1); data[data_top++] = alloc_items((pick(&r) % max_item) + 1, &r);
} }
else { else {
// 25% retain // 25% retain
retained[retain_top++] = alloc_items( 10*((rand() % max_item_retained) + 1) ); retained[retain_top++] = alloc_items(10*((pick(&r) % max_item_retained) + 1), &r);
retain--; retain--;
} }
if ((rand()%3)!=0 && data_top > 0) { if (chance(66, &r) && data_top > 0) {
// 66% free previous alloc // 66% free previous alloc
size_t idx = rand() % data_top; size_t idx = pick(&r) % data_top;
free_items(data[idx]); free_items(data[idx]);
data[idx]=NULL; data[idx] = NULL;
} }
if ((tid%2)==0 && (rand()%4)==0 && data_top > 0) { if (chance(25, &r) && data_top > 0) {
// 25% transfer-swap of half the threads // 25% transfer-swap
size_t data_idx = rand() % data_top; size_t data_idx = pick(&r) % data_top;
size_t transfer_idx = rand() % TRANSFERS; size_t transfer_idx = pick(&r) % TRANSFERS;
void* p = data[data_idx]; void* p = data[data_idx];
void* q = mi_atomic_exchange_ptr(&transfer[transfer_idx],p); void* q = atomic_exchange_ptr(&transfer[transfer_idx], p);
data[data_idx] = q; data[data_idx] = q;
} }
} }
@ -106,20 +137,33 @@ static void stress(intptr_t tid) {
} }
mi_free(retained); mi_free(retained);
mi_free(data); mi_free(data);
//bench_end_thread();
} }
static void run_os_threads(); static void run_os_threads(size_t nthreads);
int main() { int main(int argc, char** argv) {
srand(42); if (argc>=2) {
memset((void*)transfer,0,TRANSFERS*sizeof(void*)); char* end;
run_os_threads(); long n = strtol(argv[1], &end, 10);
if (n > 0) THREADS = n;
}
if (argc>=3) {
char* end;
long n = (strtol(argv[2], &end, 10));
if (n > 0) N = n;
}
printf("start with %i threads with a %i%% load-per-thread\n", THREADS, N);
//bench_start_program();
memset((void*)transfer, 0, TRANSFERS*sizeof(void*));
run_os_threads(THREADS);
for (int i = 0; i < TRANSFERS; i++) { for (int i = 0; i < TRANSFERS; i++) {
free_items((void*)transfer[i]); free_items((void*)transfer[i]);
} }
mi_collect(false); // ensures abandoned segments are reclaimed mi_collect(false);
mi_collect(true); // frees everything mi_collect(true);
mi_stats_print(NULL); mi_stats_print(NULL);
//bench_end_program();
return 0; return 0;
} }
@ -133,36 +177,48 @@ static DWORD WINAPI thread_entry(LPVOID param) {
return 0; return 0;
} }
static void run_os_threads() { static void run_os_threads(size_t nthreads) {
DWORD tids[THREADS]; DWORD* tids = (DWORD*)malloc(nthreads * sizeof(DWORD));
HANDLE thandles[THREADS]; HANDLE* thandles = (HANDLE*)malloc(nthreads * sizeof(HANDLE));
for(intptr_t i = 0; i < THREADS; i++) { for (uintptr_t i = 0; i < nthreads; i++) {
thandles[i] = CreateThread(0,4096,&thread_entry,(void*)(i),0,&tids[i]); thandles[i] = CreateThread(0, 4096, &thread_entry, (void*)(i), 0, &tids[i]);
} }
for (int i = 0; i < THREADS; i++) { for (size_t i = 0; i < nthreads; i++) {
WaitForSingleObject(thandles[i], INFINITE); WaitForSingleObject(thandles[i], INFINITE);
} }
} }
static void* atomic_exchange_ptr(volatile void** p, void* newval) {
#if (INTPTR_MAX == UINT32_MAX)
return (void*)InterlockedExchange((volatile LONG*)p, (LONG)newval);
#else
return (void*)InterlockedExchange64((volatile LONG64*)p, (LONG64)newval);
#endif
}
#else #else
#include <pthread.h> #include <pthread.h>
#include <stdatomic.h>
static void* thread_entry( void* param ) { static void* thread_entry(void* param) {
stress((uintptr_t)param); stress((uintptr_t)param);
return NULL; return NULL;
} }
static void run_os_threads() { static void run_os_threads(size_t nthreads) {
pthread_t threads[THREADS]; pthread_t* threads = (pthread_t*)mi_malloc(nthreads*sizeof(pthread_t));
memset(threads,0,sizeof(pthread_t)*THREADS); memset(threads, 0, sizeof(pthread_t)*nthreads);
//pthread_setconcurrency(THREADS); //pthread_setconcurrency(nthreads);
for(uintptr_t i = 0; i < THREADS; i++) { for (uintptr_t i = 0; i < nthreads; i++) {
pthread_create(&threads[i], NULL, &thread_entry, (void*)i); pthread_create(&threads[i], NULL, &thread_entry, (void*)i);
} }
for (size_t i = 0; i < THREADS; i++) { for (size_t i = 0; i < nthreads; i++) {
pthread_join(threads[i], NULL); pthread_join(threads[i], NULL);
} }
} }
static void* atomic_exchange_ptr(volatile void** p, void* newval) {
return atomic_exchange_explicit((volatile _Atomic(void*)*)p, newval, memory_order_acquire);
}
#endif #endif