Remove custom pool memory allocator per wesnoth-dev mailing list discussion
This affects the cmake and scons scripts, and various project files in projectfiles/. I have only tested my changes to scons (and loonycyborg might still want to review the diff regarding a line in src/SConscript involving game.cpp).
This commit is contained in:
parent
5f79fb5439
commit
20bcf49f3e
12 changed files with 6 additions and 5644 deletions
|
@ -76,7 +76,6 @@ option(ENABLE_SERVER "Enable compilation of server" ON)
|
|||
option(ENABLE_TOOLS "Enable building and installation of tools for artists and WML maintainers")
|
||||
option(ENABLE_TESTS "Build unit tests")
|
||||
option(ENABLE_NLS "Enable building of translations" ON)
|
||||
option(ENABLE_POOL_ALLOC "Enable custom pool malloc" OFF)
|
||||
option(ENABLE_LOW_MEM "Reduce memory usage by removing extra functionality" OFF)
|
||||
option(ENABLE_OMP "Enables OpenMP, and has additional dependencies" OFF)
|
||||
|
||||
|
@ -150,10 +149,6 @@ if(NOT WIN32)
|
|||
add_definitions(-DWESNOTH_PATH=\\\"${DATADIR}\\\")
|
||||
endif(NOT WIN32)
|
||||
|
||||
if(NOT ENABLE_POOL_ALLOC OR WIN32)
|
||||
add_definitions(-DDISABLE_POOL_ALLOC)
|
||||
endif(NOT ENABLE_POOL_ALLOC OR WIN32)
|
||||
|
||||
if(X11_FOUND)
|
||||
add_definitions(-D_X11)
|
||||
endif(X11_FOUND)
|
||||
|
|
|
@ -80,7 +80,6 @@ opts.AddVariables(
|
|||
BoolVariable('raw_sockets', 'Set to use raw receiving sockets in the multiplayer network layer rather than the SDL_net facilities', False),
|
||||
BoolVariable('forum_user_handler', 'Enable forum user handler in wesnothd', False),
|
||||
BoolVariable('use_network_ana', 'Use the new network api', False),
|
||||
BoolVariable('pool_alloc', 'Enable custom pool malloc', False),
|
||||
('server_gid', 'group id of the user who runs wesnothd', ""),
|
||||
('server_uid', 'user id of the user who runs wesnothd', ""),
|
||||
BoolVariable('strict', 'Set to strict compilation', False),
|
||||
|
@ -425,9 +424,6 @@ for env in [test_env, client_env, env]:
|
|||
if env['internal_data']:
|
||||
env.Append(CPPDEFINES = "USE_INTERNAL_DATA")
|
||||
|
||||
if env["PLATFORM"] == "win32":
|
||||
env["pool_alloc"] = False
|
||||
|
||||
if have_X:
|
||||
env.Append(CPPDEFINES = "_X11")
|
||||
|
||||
|
|
|
@ -211,9 +211,7 @@
|
|||
<Unit filename="../../src/formula_string_utils.hpp" />
|
||||
<Unit filename="../../src/formula_tokenizer.cpp" />
|
||||
<Unit filename="../../src/formula_tokenizer.hpp" />
|
||||
<Unit filename="../../src/game.cpp">
|
||||
<Option compiler="gcc" use="1" buildCommand="$compiler $options -DDISABLE_POOL_ALLOC $includes -c $file -o $object" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/game.cpp" />
|
||||
<Unit filename="../../src/game_config.cpp" />
|
||||
<Unit filename="../../src/game_config.hpp" />
|
||||
<Unit filename="../../src/game_display.cpp" />
|
||||
|
@ -517,10 +515,6 @@
|
|||
<Unit filename="../../src/lobby_preferences.hpp" />
|
||||
<Unit filename="../../src/log.cpp" />
|
||||
<Unit filename="../../src/log.hpp" />
|
||||
<Unit filename="../../src/malloc.c">
|
||||
<Option compilerVar="CC" />
|
||||
<Option compiler="gcc" use="1" buildCommand="$compiler $options -DDISABLE_POOL_ALLOC $includes -c $file -o $object" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/map.cpp" />
|
||||
<Unit filename="../../src/map.hpp" />
|
||||
<Unit filename="../../src/map_create.cpp" />
|
||||
|
@ -584,10 +578,6 @@
|
|||
<Unit filename="../../src/playsingle_controller.hpp" />
|
||||
<Unit filename="../../src/playturn.cpp" />
|
||||
<Unit filename="../../src/playturn.hpp" />
|
||||
<Unit filename="../../src/poolalloc.c">
|
||||
<Option compilerVar="CC" />
|
||||
<Option compiler="gcc" use="1" buildCommand="$compiler $options -DDISABLE_POOL_ALLOC $includes -c $file -o $object" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/portrait.cpp" />
|
||||
<Unit filename="../../src/portrait.hpp" />
|
||||
<Unit filename="../../src/preferences.cpp" />
|
||||
|
|
|
@ -253,9 +253,7 @@
|
|||
<Unit filename="..\..\src\formula_string_utils.hpp" />
|
||||
<Unit filename="..\..\src\formula_tokenizer.cpp" />
|
||||
<Unit filename="..\..\src\formula_tokenizer.hpp" />
|
||||
<Unit filename="..\..\src\game.cpp">
|
||||
<Option compiler="gcc" use="1" buildCommand="$compiler $options -DDISABLE_POOL_ALLOC $includes -c $file -o $object" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\game.cpp" />
|
||||
<Unit filename="..\..\src\game_config.cpp" />
|
||||
<Unit filename="..\..\src\game_config.hpp" />
|
||||
<Unit filename="..\..\src\game_controller.cpp" />
|
||||
|
@ -641,10 +639,6 @@
|
|||
<Unit filename="..\..\src\lua\lzio.h" />
|
||||
<Unit filename="..\..\src\lua_jailbreak_exception.cpp" />
|
||||
<Unit filename="..\..\src\lua_jailbreak_exception.hpp" />
|
||||
<Unit filename="..\..\src\malloc.c">
|
||||
<Option compilerVar="CC" />
|
||||
<Option compiler="gcc" use="1" buildCommand="$compiler $options -DDISABLE_POOL_ALLOC $includes -c $file -o $object" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\map.cpp" />
|
||||
<Unit filename="..\..\src\map.hpp" />
|
||||
<Unit filename="..\..\src\map_create.cpp" />
|
||||
|
@ -713,10 +707,6 @@
|
|||
<Unit filename="..\..\src\playsingle_controller.hpp" />
|
||||
<Unit filename="..\..\src\playturn.cpp" />
|
||||
<Unit filename="..\..\src\playturn.hpp" />
|
||||
<Unit filename="..\..\src\poolalloc.c">
|
||||
<Option compilerVar="CC" />
|
||||
<Option compiler="gcc" use="1" buildCommand="$compiler $options -DDISABLE_POOL_ALLOC $includes -c $file -o $object" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\portrait.cpp" />
|
||||
<Unit filename="..\..\src\portrait.hpp" />
|
||||
<Unit filename="..\..\src\preferences.cpp" />
|
||||
|
|
|
@ -1310,11 +1310,7 @@
|
|||
<ClCompile Include="..\..\src\formula_function.cpp" />
|
||||
<ClCompile Include="..\..\src\formula_string_utils.cpp" />
|
||||
<ClCompile Include="..\..\src\formula_tokenizer.cpp" />
|
||||
<ClCompile Include="..\..\src\game.cpp">
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug (fast)|Win32'">DISABLE_POOL_ALLOC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">DISABLE_POOL_ALLOC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">DISABLE_POOL_ALLOC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\game.cpp" />
|
||||
<ClCompile Include="..\..\src\game_controller.cpp" />
|
||||
<ClCompile Include="..\..\src\game_controller_abstract.cpp" />
|
||||
<ClCompile Include="..\..\src\game_controller_new.cpp" />
|
||||
|
|
|
@ -15130,54 +15130,6 @@
|
|||
<File
|
||||
RelativePath="..\..\src\game.cpp"
|
||||
>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="DISABLE_POOL_ALLOC"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="DISABLE_POOL_ALLOC"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug (fast)|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="DISABLE_POOL_ALLOC"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug_with_VLD|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="DISABLE_POOL_ALLOC"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Test_Debug|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="DISABLE_POOL_ALLOC"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Test_Release|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="DISABLE_POOL_ALLOC"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\game_config.hpp"
|
||||
|
|
|
@ -323,7 +323,7 @@
|
|||
B5599B690EC62181008DD061 /* game_events.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5599A7B0EC62181008DD061 /* game_events.cpp */; };
|
||||
B5599B6A0EC62181008DD061 /* game_display.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5599A7E0EC62181008DD061 /* game_display.cpp */; settings = {COMPILER_FLAGS = "-DHAVE_GROWL"; }; };
|
||||
B5599B6B0EC62181008DD061 /* game_config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5599A800EC62181008DD061 /* game_config.cpp */; settings = {COMPILER_FLAGS = "-DWESNOTH_PATH='\"./\"'"; }; };
|
||||
B5599B6C0EC62181008DD061 /* game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5599A810EC62181008DD061 /* game.cpp */; settings = {COMPILER_FLAGS = "-DDISABLE_POOL_ALLOC"; }; };
|
||||
B5599B6C0EC62181008DD061 /* game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5599A810EC62181008DD061 /* game.cpp */; };
|
||||
B5599B6D0EC62181008DD061 /* formula_tokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5599A830EC62181008DD061 /* formula_tokenizer.cpp */; };
|
||||
B5599B6E0EC62181008DD061 /* formula_function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5599A860EC62181008DD061 /* formula_function.cpp */; };
|
||||
B5599B700EC62181008DD061 /* formula.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5599A8C0EC62181008DD061 /* formula.cpp */; };
|
||||
|
|
|
@ -223,14 +223,6 @@ set(libwesnoth-core_STAT_SRC
|
|||
${REVISION_FILE}
|
||||
)
|
||||
|
||||
if(ENABLE_POOL_ALLOC AND NOT WIN32)
|
||||
set(libwesnoth-core_STAT_SRC
|
||||
${libwesnoth-core_STAT_SRC}
|
||||
malloc.c
|
||||
poolalloc.c
|
||||
)
|
||||
endif(ENABLE_POOL_ALLOC AND NOT WIN32)
|
||||
|
||||
# a 'lib' is automatically set in front when creating the library (as in the filename)
|
||||
# internal reference is the name given here
|
||||
add_library(wesnoth-core STATIC EXCLUDE_FROM_ALL ${libwesnoth-core_STAT_SRC})
|
||||
|
|
|
@ -35,11 +35,6 @@ libwesnoth_core_sources = Split("""
|
|||
serialization/validator.cpp
|
||||
tools/schema/tag.cpp
|
||||
""")
|
||||
if env["pool_alloc"]:
|
||||
libwesnoth_core_sources.extend(Split("""
|
||||
malloc.c
|
||||
poolalloc.c
|
||||
"""))
|
||||
|
||||
libwesnoth_core_sources.extend(env.Object("network_worker.cpp", EXTRA_DEFINE = env['raw_sockets'] and "NETWORK_USE_RAW_SOCKETS" or None))
|
||||
|
||||
|
@ -483,7 +478,7 @@ def WesnothProgram(env, target, source, can_build, **kw):
|
|||
for env in [test_env, client_env, env]:
|
||||
env.AddMethod(WesnothProgram)
|
||||
|
||||
game_cpp = client_env.Object("game.cpp", EXTRA_DEFINE = not env["pool_alloc"] and "DISABLE_POOL_ALLOC" or None);
|
||||
game_cpp = client_env.Object("game.cpp");
|
||||
|
||||
wesnoth_objects = [game_cpp, libwesnoth_extras, libwesnoth_core, libwesnoth_sdl,
|
||||
libwesnoth, env["wesnoth_res"]]
|
||||
|
|
10
src/game.cpp
10
src/game.cpp
|
@ -582,12 +582,6 @@ static int do_gameloop(int argc, char** argv)
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_POOL_ALLOC
|
||||
extern "C" {
|
||||
void init_custom_malloc();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __native_client__
|
||||
int wesnoth_main(int argc, char** argv)
|
||||
|
@ -618,9 +612,7 @@ int main(int argc, char** argv)
|
|||
execv(argv[0], argv);
|
||||
}
|
||||
#endif
|
||||
#ifndef DISABLE_POOL_ALLOC
|
||||
init_custom_malloc();
|
||||
#endif
|
||||
|
||||
if(SDL_Init(SDL_INIT_TIMER) < 0) {
|
||||
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
|
||||
return(1);
|
||||
|
|
5068
src/malloc.c
5068
src/malloc.c
File diff suppressed because it is too large
Load diff
468
src/poolalloc.c
468
src/poolalloc.c
|
@ -1,468 +0,0 @@
|
|||
/* $Id$ */
|
||||
#ifndef DISABLE_POOL_ALLOC
|
||||
/*
|
||||
Copyright (C) 2008 by David White <dave@whitevine.net>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
or at your option any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
This file defines a custom allocator that is optimized for doing memory
|
||||
allocations for Wesnoth. Its primary consideration is space, though it
|
||||
should be pretty fast too.
|
||||
|
||||
The largest consideration is meta-data: Wesnoth allocates many small chunks,
|
||||
and so we want to minimize per-chunk meta-data. Typical general-purpose
|
||||
allocators have a per-chunk overhead of one or two pointers. This allocator
|
||||
has no per-chunk overhead, just memory overhead of less than 2%.
|
||||
|
||||
The allocator is designed to handle small chunks. We include dlmalloc's source,
|
||||
and allocations that are not considered small are simply punted to dlmalloc
|
||||
to allocate.
|
||||
|
||||
Some basic terminology:
|
||||
|
||||
- chunk: a single allocation, allocated with malloc.
|
||||
- block: a block of memory from which we allocate chunks. A block has a header
|
||||
and then its data section. A block should be a multiple of the page size.
|
||||
A given block is dedicated to allocating chunks of a specific size. All blocks
|
||||
are the same size (4096 bytes by default, which should be the minimum).
|
||||
- superblock: we allocate one huge block from which all blocks are allocated.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void* dlmalloc(size_t size);
|
||||
void* dlcalloc(size_t count, size_t size);
|
||||
void* dlvalloc(size_t size);
|
||||
void* dlmemalign(size_t alignment, size_t size);
|
||||
void* dlrealloc(void* ptr, size_t size);
|
||||
void dlfree(void* ptr);
|
||||
|
||||
#define BLOCK_SIZE (4096)
|
||||
|
||||
#define MAX_CHUNK_SIZE (256)
|
||||
#define CHUNK_SIZE_STEP (sizeof(void*))
|
||||
#define NUM_POOLS ((MAX_CHUNK_SIZE/CHUNK_SIZE_STEP) + 1)
|
||||
|
||||
// find the index of the pool that a chunk of size n will be allocated from.
|
||||
#define GET_POOL_INDEX(n) ((n)/CHUNK_SIZE_STEP)
|
||||
#define ROUNDUP_SIZE(n) (((n)%CHUNK_SIZE_STEP) ? ((n) + CHUNK_SIZE_STEP - ((n)%CHUNK_SIZE_STEP)) : (n))
|
||||
|
||||
#define CUSTOM_MEMORY_SIZE (1024*1024*40)
|
||||
uint8_t* begin_superblock_range = NULL;
|
||||
uint8_t* begin_superblock = NULL;
|
||||
uint8_t* end_superblock = NULL;
|
||||
#define IS_OUR_PTR(ptr) ((uint8_t*)(ptr) >= begin_superblock_range && (uint8_t*)(ptr) < end_superblock)
|
||||
|
||||
pthread_t main_thread;
|
||||
|
||||
void init_custom_malloc()
|
||||
{
|
||||
main_thread = pthread_self();
|
||||
|
||||
// allocate the memory -- allocate an extra block at the end, so that
|
||||
// if the address we get back isn't block-aligned, we can advance
|
||||
// the pointer until it is.
|
||||
void* alloc = dlmalloc(CUSTOM_MEMORY_SIZE + BLOCK_SIZE);
|
||||
assert(alloc);
|
||||
begin_superblock = (uint8_t*)alloc;
|
||||
while(((uintptr_t)begin_superblock)%BLOCK_SIZE) {
|
||||
++begin_superblock;
|
||||
}
|
||||
|
||||
end_superblock = begin_superblock + CUSTOM_MEMORY_SIZE;
|
||||
begin_superblock_range = begin_superblock;
|
||||
}
|
||||
|
||||
typedef struct BlockHeader {
|
||||
uint32_t check_a;
|
||||
struct Block* next;
|
||||
struct Block* prev;
|
||||
uint8_t* uninit;
|
||||
void* free_list;
|
||||
uint32_t chunk_size;
|
||||
uint32_t allocated_chunks;
|
||||
uint32_t check_b;
|
||||
} BlockHeader;
|
||||
|
||||
typedef struct Block {
|
||||
BlockHeader header;
|
||||
char data[BLOCK_SIZE - sizeof(BlockHeader)];
|
||||
} Block;
|
||||
|
||||
#define BLOCK_EMPTY(b) ((b)->header.allocated_chunks == 0)
|
||||
#define BLOCK_FULL(b) ((b)->header.uninit == NULL && (b)->header.free_list == NULL)
|
||||
|
||||
void* allocate_chunk_from_block(Block* b)
|
||||
{
|
||||
b->header.allocated_chunks++;
|
||||
if(b->header.uninit) {
|
||||
void* result = b->header.uninit;
|
||||
b->header.uninit += b->header.chunk_size;
|
||||
|
||||
//check if we've run out of uninitialized elements.
|
||||
if(b->header.uninit + b->header.chunk_size > (uint8_t*)(b+1)) {
|
||||
b->header.uninit = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
assert(b->header.free_list);
|
||||
void* result = b->header.free_list;
|
||||
b->header.free_list = *(void**)result;
|
||||
return result;
|
||||
}
|
||||
|
||||
// inline causes linker errors in debug builds with gcc 4.3.2(-std=c99 and -O0)
|
||||
#ifdef DEBUG
|
||||
#define inline
|
||||
#endif
|
||||
inline Block* get_block_from_chunk(void* chunk)
|
||||
#ifdef DEBUG
|
||||
#undef inline
|
||||
#endif
|
||||
{
|
||||
int8_t* block_ptr = ((int8_t*)chunk) - ((uintptr_t)chunk)%BLOCK_SIZE;
|
||||
return (Block*)block_ptr;
|
||||
}
|
||||
|
||||
Block* free_chunk_from_block(void* chunk)
|
||||
{
|
||||
Block* block = get_block_from_chunk(chunk);
|
||||
block->header.allocated_chunks--;
|
||||
*(void**)chunk = block->header.free_list;
|
||||
block->header.free_list = chunk;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
Block* format_block(Block* ptr, int chunk_size)
|
||||
{
|
||||
BlockHeader* block = &ptr->header;
|
||||
|
||||
block->check_a = 0xFFFFFFFF;
|
||||
block->check_b = 0xFFFFFFFF;
|
||||
|
||||
block->next = NULL;
|
||||
block->prev = NULL;
|
||||
block->uninit = (uint8_t*)(block+1);
|
||||
block->free_list = NULL;
|
||||
block->chunk_size = chunk_size;
|
||||
block->allocated_chunks = 0;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
Block* block_free_list = NULL;
|
||||
|
||||
Block* allocate_new_block(uint32_t chunk_size)
|
||||
{
|
||||
if(block_free_list == NULL && begin_superblock >= end_superblock) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Block* block;
|
||||
|
||||
if(block_free_list != NULL) {
|
||||
block = block_free_list;
|
||||
block_free_list = block->header.next;
|
||||
} else {
|
||||
block = (Block*)begin_superblock;
|
||||
begin_superblock += sizeof(Block);
|
||||
}
|
||||
|
||||
return format_block(block, chunk_size);
|
||||
}
|
||||
|
||||
void return_block_to_free_list(Block* block)
|
||||
{
|
||||
block->header.next = block_free_list;
|
||||
block_free_list = block;
|
||||
}
|
||||
|
||||
Block* block_pools[NUM_POOLS];
|
||||
|
||||
#define IS_BLOCK_ORPHAN(block) ((block)->header.next == NULL && (block)->header.prev == NULL && block_pools[GET_POOL_INDEX(block->header.chunk_size)] != (block))
|
||||
|
||||
void add_block_to_pool(Block* block)
|
||||
{
|
||||
assert(block->header.chunk_size > 0 && block->header.chunk_size <= MAX_CHUNK_SIZE);
|
||||
Block** target = &block_pools[GET_POOL_INDEX(block->header.chunk_size)];
|
||||
block->header.next = *target;
|
||||
block->header.prev = NULL;
|
||||
if(*target) {
|
||||
(*target)->header.prev = block;
|
||||
}
|
||||
*target = block;
|
||||
}
|
||||
|
||||
void make_block_orphan(Block* block)
|
||||
{
|
||||
BlockHeader* header = &block->header;
|
||||
if(block_pools[GET_POOL_INDEX(header->chunk_size)] == block) {
|
||||
block_pools[GET_POOL_INDEX(header->chunk_size)] = header->next;
|
||||
}
|
||||
|
||||
if(header->prev) {
|
||||
header->prev->header.next = header->next;
|
||||
}
|
||||
|
||||
if(header->next) {
|
||||
header->next->header.prev = header->prev;
|
||||
}
|
||||
|
||||
header->prev = NULL;
|
||||
header->next = NULL;
|
||||
}
|
||||
|
||||
// A list of the chunks that were allocated in the main thread, but free()
|
||||
// was called in another thread. We can't deallocate them from another thread,
|
||||
// so we put them in this array. The main thread will free all these chunks,
|
||||
// whenever it can't immediately allocate memory.
|
||||
void** free_chunks;
|
||||
size_t nfree_chunks, capacity_free_chunks;
|
||||
pthread_mutex_t free_chunks_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
//mutex to protect all calls to dlmalloc.
|
||||
pthread_mutex_t dlmalloc_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void free_memory(void* ptr);
|
||||
|
||||
void collect_memory_from_other_threads()
|
||||
{
|
||||
pthread_mutex_lock(&free_chunks_mutex);
|
||||
int n;
|
||||
for(n = 0; n != nfree_chunks; ++n) {
|
||||
free_memory(free_chunks[n]);
|
||||
}
|
||||
|
||||
nfree_chunks = 0;
|
||||
pthread_mutex_unlock(&free_chunks_mutex);
|
||||
}
|
||||
|
||||
void free_memory_from_other_thread(void* ptr)
|
||||
{
|
||||
pthread_mutex_lock(&free_chunks_mutex);
|
||||
|
||||
if(nfree_chunks == capacity_free_chunks) {
|
||||
capacity_free_chunks *= 2;
|
||||
if(capacity_free_chunks < 16) {
|
||||
capacity_free_chunks = 16;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&dlmalloc_mutex);
|
||||
void** new_free_chunks = (void**)dlrealloc(free_chunks, sizeof(void*)*capacity_free_chunks);
|
||||
pthread_mutex_unlock(&dlmalloc_mutex);
|
||||
if(!new_free_chunks) {
|
||||
pthread_mutex_unlock(&free_chunks_mutex);
|
||||
fprintf(stderr, "DLREALLOC FAILED!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
free_chunks = new_free_chunks;
|
||||
}
|
||||
|
||||
free_chunks[nfree_chunks++] = ptr;
|
||||
pthread_mutex_unlock(&free_chunks_mutex);
|
||||
}
|
||||
|
||||
Block* get_block(uint32_t chunk_size)
|
||||
{
|
||||
const int index = GET_POOL_INDEX(chunk_size);
|
||||
assert(index >= 0 && index < sizeof(block_pools)/sizeof(*block_pools));
|
||||
if(block_pools[index]) {
|
||||
return block_pools[index];
|
||||
}
|
||||
|
||||
// free memory from other threads and then try again. This requires a mutex
|
||||
// lock, but this code should be rarely reached.
|
||||
collect_memory_from_other_threads();
|
||||
|
||||
if(block_pools[index]) {
|
||||
return block_pools[index];
|
||||
}
|
||||
|
||||
Block* block = allocate_new_block(chunk_size);
|
||||
if(block == NULL) {
|
||||
return block;
|
||||
}
|
||||
add_block_to_pool(block);
|
||||
return block;
|
||||
}
|
||||
|
||||
void* allocate_memory(int32_t size)
|
||||
{
|
||||
Block* block = get_block(size);
|
||||
if(block == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* result = allocate_chunk_from_block(block);
|
||||
if(BLOCK_FULL(block)) {
|
||||
make_block_orphan(block);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void free_memory(void* ptr)
|
||||
{
|
||||
Block* block = free_chunk_from_block(ptr);
|
||||
if(IS_BLOCK_ORPHAN(block)) {
|
||||
add_block_to_pool(block);
|
||||
} else if(BLOCK_EMPTY(block)) {
|
||||
//since the block is empty, return it to the global free list of
|
||||
//blocks, so it can be moved to a different pool.
|
||||
make_block_orphan(block);
|
||||
return_block_to_free_list(block);
|
||||
}
|
||||
}
|
||||
|
||||
void* malloc(size_t size)
|
||||
{
|
||||
if(pthread_equal(pthread_self(), main_thread) && size > 0 && size <= MAX_CHUNK_SIZE) {
|
||||
size = ROUNDUP_SIZE(size);
|
||||
void* result = allocate_memory(size);
|
||||
if(result != NULL) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&dlmalloc_mutex);
|
||||
void* result = dlmalloc(size);
|
||||
pthread_mutex_unlock(&dlmalloc_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
void* calloc(size_t count, size_t size)
|
||||
{
|
||||
pthread_mutex_lock(&dlmalloc_mutex);
|
||||
void* result = dlcalloc(count, size);
|
||||
pthread_mutex_unlock(&dlmalloc_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
void* valloc(size_t size)
|
||||
{
|
||||
pthread_mutex_lock(&dlmalloc_mutex);
|
||||
void* result = dlvalloc(size);
|
||||
pthread_mutex_unlock(&dlmalloc_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
void* memalign(size_t alignment, size_t size)
|
||||
{
|
||||
pthread_mutex_lock(&dlmalloc_mutex);
|
||||
void* result = dlmemalign(alignment, size);
|
||||
pthread_mutex_unlock(&dlmalloc_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Note this function might not be entirely POSIX compatible, but it seems to
|
||||
// work.
|
||||
int posix_memalign(void **memptr, size_t alignment, size_t size)
|
||||
{
|
||||
pthread_mutex_lock(&dlmalloc_mutex);
|
||||
*memptr = dlmemalign(alignment, size);
|
||||
pthread_mutex_unlock(&dlmalloc_mutex);
|
||||
return errno;
|
||||
}
|
||||
|
||||
void* realloc(void* ptr, size_t size)
|
||||
{
|
||||
if(IS_OUR_PTR(ptr)) {
|
||||
if(size == 0) {
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* new_memory = malloc(size);
|
||||
if(new_memory == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int old_size = get_block_from_chunk(ptr)->header.chunk_size;
|
||||
const size_t nbytes = size < old_size ? size : old_size;
|
||||
memcpy(new_memory, ptr, nbytes);
|
||||
free(ptr);
|
||||
return new_memory;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&dlmalloc_mutex);
|
||||
void* result = dlrealloc(ptr, size);
|
||||
pthread_mutex_unlock(&dlmalloc_mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
void free(void* ptr)
|
||||
{
|
||||
if(IS_OUR_PTR(ptr)) {
|
||||
if(!pthread_equal(pthread_self(), main_thread)) {
|
||||
//this will queue up the free to be performed later in the
|
||||
//main thread when it wants more memory.
|
||||
free_memory_from_other_thread(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
free_memory(ptr);
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&dlmalloc_mutex);
|
||||
dlfree(ptr);
|
||||
pthread_mutex_unlock(&dlmalloc_mutex);
|
||||
}
|
||||
|
||||
#ifdef TEST_POOLED_ALLOC
|
||||
int main()
|
||||
{
|
||||
init_custom_malloc();
|
||||
|
||||
void** items = NULL;
|
||||
int number_of_items = 0;
|
||||
|
||||
while(number_of_items < 100000) {
|
||||
if(number_of_items) {
|
||||
int clear = rand()%number_of_items;
|
||||
while(--clear >= 0) {
|
||||
int len = rand()%1000;
|
||||
free(items[clear]);
|
||||
items[clear] = malloc(len);
|
||||
assert(items[clear]);
|
||||
memset(items[clear], 10, len);
|
||||
}
|
||||
}
|
||||
|
||||
int i = number_of_items;
|
||||
number_of_items += rand()%100;
|
||||
items = realloc(items, sizeof(*items)*number_of_items);
|
||||
while(i != number_of_items) {
|
||||
int len = rand()%1000;
|
||||
items[i] = malloc(len);
|
||||
assert(items[i]);
|
||||
memset(items[i], 10, len);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
while(number_of_items--) {
|
||||
free(items[number_of_items]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
Loading…
Add table
Reference in a new issue