mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-11-11 07:11:39 +00:00
nedmalloc, from http://www.nedprod.com/programs/portable/nedmalloc/
greatly speeds up memory allocations git-svn-id: https://svn.eduke32.com/eduke32@1424 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
parent
9973478a9d
commit
fab4638ca8
8 changed files with 6942 additions and 3 deletions
|
@ -133,7 +133,8 @@ ENGINEOBJS+= \
|
|||
$(OBJ)/md4.$o \
|
||||
$(OBJ)/osd.$o \
|
||||
$(OBJ)/pragmas.$o \
|
||||
$(OBJ)/scriptfile.$o
|
||||
$(OBJ)/scriptfile.$o \
|
||||
$(OBJ)/nedmalloc.$o
|
||||
|
||||
ifeq (1,$(USE_OPENGL))
|
||||
ENGINEOBJS+= $(OBJ)/mdsprite.$o
|
||||
|
|
|
@ -33,6 +33,7 @@ $(OBJ)/winlayer.$o: $(SRC)/winlayer.c $(INC)/compat.h $(INC)/winlayer.h $(INC)/b
|
|||
$(OBJ)/gtkbits.$o: $(SRC)/gtkbits.c $(INC)/baselayer.h $(INC)/build.h $(INC)/dynamicgtk.h
|
||||
$(OBJ)/dynamicgtk.$o: $(SRC)/dynamicgtk.c $(INC)/dynamicgtk.h
|
||||
$(OBJ)/polymer.$o: $(SRC)/polymer.c $(INC)/polymer.h $(INC)/compat.h $(INC)/build.h $(INC)/glbuild.h $(INC)/osd.h $(INC)/pragmas.h $(INC)/mdsprite.h $(INC)/polymost.h
|
||||
$(OBJ)/nedmalloc.$o: $(SRC)/nedmalloc.c $(INC)/nedmalloc.h $(INC)/malloc.c.h
|
||||
|
||||
$(OBJ)/buildres.$(res): $(SRC)/misc/buildres.rc $(INC)/startwin.editor.h
|
||||
$(OBJ)/startwin.editor.$o: $(SRC)/startwin.editor.c $(INC)/build.h $(INC)/editor.h $(INC)/winlayer.h $(INC)/compat.h $(INC)/startwin.editor.h
|
||||
|
|
|
@ -79,7 +79,8 @@ ENGINEOBJS= \
|
|||
$(OBJ)\pragmas.$o \
|
||||
$(OBJ)\scriptfile.$o \
|
||||
$(OBJ)\winlayer.$o \
|
||||
$(OBJ)\polymer.$o
|
||||
$(OBJ)\polymer.$o \
|
||||
$(OBJ)\nedmalloc.$o
|
||||
|
||||
EDITOROBJS=$(OBJ)\build.$o \
|
||||
$(OBJ)\startwin.editor.$o \
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
# include "msvc/inttypes.h" // from http://code.google.com/p/msinttypes/
|
||||
#endif
|
||||
|
||||
#define REPLACE_SYSTEM_ALLOCATOR
|
||||
#include "nedmalloc.h"
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
|
5753
polymer/eduke32/build/include/malloc.c.h
Normal file
5753
polymer/eduke32/build/include/malloc.c.h
Normal file
File diff suppressed because it is too large
Load diff
194
polymer/eduke32/build/include/nedmalloc.h
Normal file
194
polymer/eduke32/build/include/nedmalloc.h
Normal file
|
@ -0,0 +1,194 @@
|
|||
/* nedalloc, an alternative malloc implementation for multiple threads without
|
||||
lock contention based on dlmalloc v2.8.3. (C) 2005 Niall Douglas
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef NEDMALLOC_H
|
||||
#define NEDMALLOC_H
|
||||
|
||||
|
||||
/* See malloc.c.h for what each function does.
|
||||
|
||||
REPLACE_SYSTEM_ALLOCATOR causes nedalloc's functions to be called malloc,
|
||||
free etc. instead of nedmalloc, nedfree etc. You may or may not want this.
|
||||
|
||||
NO_NED_NAMESPACE prevents the functions from being defined in the nedalloc
|
||||
namespace when in C++ (uses the global namespace instead).
|
||||
|
||||
EXTSPEC can be defined to be __declspec(dllexport) or
|
||||
__attribute__ ((visibility("default"))) or whatever you like. It defaults
|
||||
to extern.
|
||||
|
||||
USE_LOCKS can be 2 if you want to define your own MLOCK_T, INITIAL_LOCK,
|
||||
ACQUIRE_LOCK, RELEASE_LOCK, TRY_LOCK, IS_LOCKED and NULL_LOCK_INITIALIZER.
|
||||
|
||||
*/
|
||||
|
||||
#include <stddef.h> /* for size_t */
|
||||
|
||||
#ifndef EXTSPEC
|
||||
#define EXTSPEC extern
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER>=1400
|
||||
#define MALLOCATTR __declspec(restrict)
|
||||
#endif
|
||||
#ifdef __GNUC__
|
||||
#define MALLOCATTR __attribute__ ((malloc))
|
||||
#endif
|
||||
#ifndef MALLOCATTR
|
||||
#define MALLOCATTR
|
||||
#endif
|
||||
|
||||
#ifdef REPLACE_SYSTEM_ALLOCATOR
|
||||
#define nedmalloc malloc
|
||||
#define nedcalloc calloc
|
||||
#define nedrealloc realloc
|
||||
#define nedfree free
|
||||
#define nedmemalign memalign
|
||||
#define nedmallinfo mallinfo
|
||||
#define nedmallopt mallopt
|
||||
#define nedmalloc_trim malloc_trim
|
||||
#define nedmalloc_stats malloc_stats
|
||||
#define nedmalloc_footprint malloc_footprint
|
||||
#define nedindependent_calloc independent_calloc
|
||||
#define nedindependent_comalloc independent_comalloc
|
||||
#ifdef _MSC_VER
|
||||
#define nedblksize _msize
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NO_MALLINFO
|
||||
#define NO_MALLINFO 0
|
||||
#endif
|
||||
|
||||
#if !NO_MALLINFO
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
struct mallinfo;
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#if !defined(NO_NED_NAMESPACE)
|
||||
namespace nedalloc {
|
||||
#else
|
||||
extern "C" {
|
||||
#endif
|
||||
#define THROWSPEC throw()
|
||||
#else
|
||||
#define THROWSPEC
|
||||
#endif
|
||||
|
||||
/* These are the global functions */
|
||||
|
||||
/* Gets the usable size of an allocated block. Note this will always be bigger than what was
|
||||
asked for due to rounding etc.
|
||||
*/
|
||||
EXTSPEC size_t nedblksize(void *mem) THROWSPEC;
|
||||
|
||||
EXTSPEC void nedsetvalue(void *v) THROWSPEC;
|
||||
|
||||
EXTSPEC MALLOCATTR void * nedmalloc(size_t size) THROWSPEC;
|
||||
EXTSPEC MALLOCATTR void * nedcalloc(size_t no, size_t size) THROWSPEC;
|
||||
EXTSPEC MALLOCATTR void * nedrealloc(void *mem, size_t size) THROWSPEC;
|
||||
EXTSPEC void nedfree(void *mem) THROWSPEC;
|
||||
EXTSPEC MALLOCATTR void * nedmemalign(size_t alignment, size_t bytes) THROWSPEC;
|
||||
#if !NO_MALLINFO
|
||||
EXTSPEC struct mallinfo nedmallinfo(void) THROWSPEC;
|
||||
#endif
|
||||
EXTSPEC int nedmallopt(int parno, int value) THROWSPEC;
|
||||
EXTSPEC int nedmalloc_trim(size_t pad) THROWSPEC;
|
||||
EXTSPEC void nedmalloc_stats(void) THROWSPEC;
|
||||
EXTSPEC size_t nedmalloc_footprint(void) THROWSPEC;
|
||||
EXTSPEC MALLOCATTR void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC;
|
||||
EXTSPEC MALLOCATTR void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC;
|
||||
|
||||
/* Destroys the system memory pool used by the functions above.
|
||||
Useful for when you have nedmalloc in a DLL you're about to unload.
|
||||
If you call ANY nedmalloc functions after calling this you will
|
||||
get a fatal exception!
|
||||
*/
|
||||
EXTSPEC void neddestroysyspool() THROWSPEC;
|
||||
|
||||
/* These are the pool functions */
|
||||
struct nedpool_t;
|
||||
typedef struct nedpool_t nedpool;
|
||||
|
||||
/* Creates a memory pool for use with the nedp* functions below.
|
||||
Capacity is how much to allocate immediately (if you know you'll be allocating a lot
|
||||
of memory very soon) which you can leave at zero. Threads specifies how many threads
|
||||
will *normally* be accessing the pool concurrently. Setting this to zero means it
|
||||
extends on demand, but be careful of this as it can rapidly consume system resources
|
||||
where bursts of concurrent threads use a pool at once.
|
||||
*/
|
||||
EXTSPEC MALLOCATTR nedpool *nedcreatepool(size_t capacity, int threads) THROWSPEC;
|
||||
|
||||
/* Destroys a memory pool previously created by nedcreatepool().
|
||||
*/
|
||||
EXTSPEC void neddestroypool(nedpool *p) THROWSPEC;
|
||||
|
||||
/* Sets a value to be associated with a pool. You can retrieve this value by passing
|
||||
any memory block allocated from that pool.
|
||||
*/
|
||||
EXTSPEC void nedpsetvalue(nedpool *p, void *v) THROWSPEC;
|
||||
/* Gets a previously set value using nedpsetvalue() or zero if memory is unknown.
|
||||
Optionally can also retrieve pool.
|
||||
*/
|
||||
EXTSPEC void *nedgetvalue(nedpool **p, void *mem) THROWSPEC;
|
||||
|
||||
/* Disables the thread cache for the calling thread, returning any existing cache
|
||||
data to the central pool. Remember to ALWAYS call with zero if you used the
|
||||
system pool.
|
||||
*/
|
||||
EXTSPEC void neddisablethreadcache(nedpool *p) THROWSPEC;
|
||||
|
||||
EXTSPEC MALLOCATTR void * nedpmalloc(nedpool *p, size_t size) THROWSPEC;
|
||||
EXTSPEC MALLOCATTR void * nedpcalloc(nedpool *p, size_t no, size_t size) THROWSPEC;
|
||||
EXTSPEC MALLOCATTR void * nedprealloc(nedpool *p, void *mem, size_t size) THROWSPEC;
|
||||
EXTSPEC void nedpfree(nedpool *p, void *mem) THROWSPEC;
|
||||
EXTSPEC MALLOCATTR void * nedpmemalign(nedpool *p, size_t alignment, size_t bytes) THROWSPEC;
|
||||
#if !NO_MALLINFO
|
||||
EXTSPEC struct mallinfo nedpmallinfo(nedpool *p) THROWSPEC;
|
||||
#endif
|
||||
EXTSPEC int nedpmallopt(nedpool *p, int parno, int value) THROWSPEC;
|
||||
EXTSPEC int nedpmalloc_trim(nedpool *p, size_t pad) THROWSPEC;
|
||||
EXTSPEC void nedpmalloc_stats(nedpool *p) THROWSPEC;
|
||||
EXTSPEC size_t nedpmalloc_footprint(nedpool *p) THROWSPEC;
|
||||
EXTSPEC MALLOCATTR void **nedpindependent_calloc(nedpool *p, size_t elemsno, size_t elemsize, void **chunks) THROWSPEC;
|
||||
EXTSPEC MALLOCATTR void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **chunks) THROWSPEC;
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef MALLOCATTR
|
||||
#undef EXTSPEC
|
||||
|
||||
#endif
|
|
@ -53,6 +53,8 @@ extern float pr_specularpower;
|
|||
extern float pr_specularfactor;
|
||||
extern int32_t pr_atiworkaround;
|
||||
|
||||
extern int32_t r_pr_maxlightpasses;
|
||||
|
||||
// MATERIAL
|
||||
typedef enum {
|
||||
PR_BIT_HEADER, // must be first
|
||||
|
@ -214,7 +216,7 @@ typedef struct s_prplane {
|
|||
GLuint ivbo;
|
||||
// lights
|
||||
int16_t lights[PR_MAXLIGHTS];
|
||||
int16_t lightcount;
|
||||
uint16_t lightcount;
|
||||
} _prplane;
|
||||
|
||||
typedef struct s_prsector {
|
||||
|
@ -288,6 +290,7 @@ void polymer_drawsprite(int32_t snum);
|
|||
void polymer_setanimatesprites(animatespritesptr animatesprites, int32_t x, int32_t y, int32_t a, int32_t smoothratio);
|
||||
int16_t polymer_addlight(_prlight* light);
|
||||
void polymer_deletelight(int16_t lighti);
|
||||
void polymer_invalidatelights(void);
|
||||
|
||||
# ifdef POLYMER_C
|
||||
|
||||
|
|
983
polymer/eduke32/build/src/nedmalloc.c
Normal file
983
polymer/eduke32/build/src/nedmalloc.c
Normal file
|
@ -0,0 +1,983 @@
|
|||
/* Alternative malloc implementation for multiple threads without
|
||||
lock contention based on dlmalloc. (C) 2005-2006 Niall Douglas
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* Enable full aliasing on MSVC */
|
||||
/*#pragma optimize("a", on)*/
|
||||
#endif
|
||||
|
||||
/*#define FULLSANITYCHECKS*/
|
||||
|
||||
#include "nedmalloc.h"
|
||||
#ifdef WIN32
|
||||
#include <malloc.h>
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
#define MSPACES 1
|
||||
#define ONLY_MSPACES 1
|
||||
#ifndef USE_LOCKS
|
||||
#define USE_LOCKS 1
|
||||
#endif
|
||||
#define FOOTERS 1 /* Need to enable footers so frees lock the right mspace */
|
||||
#if defined(DEBUG) && !defined(_DEBUG)
|
||||
#define _DEBUG
|
||||
#elif !defined(NDEBUG) && !defined(DEBUG) && !defined(_DEBUG)
|
||||
#define NDEBUG
|
||||
#endif
|
||||
#undef DEBUG /* dlmalloc wants DEBUG either 0 or 1 */
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG 1
|
||||
#else
|
||||
#define DEBUG 0
|
||||
#endif
|
||||
#ifdef NDEBUG /* Disable assert checking on release builds */
|
||||
#undef DEBUG
|
||||
#endif
|
||||
/* The default of 64Kb means we spend too much time kernel-side */
|
||||
#ifndef DEFAULT_GRANULARITY
|
||||
#define DEFAULT_GRANULARITY (1*1024*1024)
|
||||
#endif
|
||||
/*#define USE_SPIN_LOCKS 0*/
|
||||
|
||||
|
||||
/*#define FORCEINLINE*/
|
||||
#include "malloc.c.h"
|
||||
#ifdef NDEBUG /* Disable assert checking on release builds */
|
||||
#undef DEBUG
|
||||
#endif
|
||||
#if defined(__GNUC__) && defined(DEBUG)
|
||||
#warning DEBUG is defined so allocator will run with assert checking! Define NDEBUG to run at full speed.
|
||||
#endif
|
||||
|
||||
/* The maximum concurrent threads in a pool possible */
|
||||
#ifndef MAXTHREADSINPOOL
|
||||
#define MAXTHREADSINPOOL 16
|
||||
#endif
|
||||
/* The maximum number of threadcaches which can be allocated */
|
||||
#ifndef THREADCACHEMAXCACHES
|
||||
#define THREADCACHEMAXCACHES 256
|
||||
#endif
|
||||
/* The maximum size to be allocated from the thread cache */
|
||||
#ifndef THREADCACHEMAX
|
||||
#define THREADCACHEMAX 8192
|
||||
#endif
|
||||
#if 0
|
||||
/* The number of cache entries for finer grained bins. This is (topbitpos(THREADCACHEMAX)-4)*2 */
|
||||
#define THREADCACHEMAXBINS ((13-4)*2)
|
||||
#else
|
||||
/* The number of cache entries. This is (topbitpos(THREADCACHEMAX)-4) */
|
||||
#define THREADCACHEMAXBINS (13-4)
|
||||
#endif
|
||||
/* Point at which the free space in a thread cache is garbage collected */
|
||||
#ifndef THREADCACHEMAXFREESPACE
|
||||
#define THREADCACHEMAXFREESPACE (512*1024)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#define TLSVAR DWORD
|
||||
#define TLSALLOC(k) (*(k)=TlsAlloc(), TLS_OUT_OF_INDEXES==*(k))
|
||||
#define TLSFREE(k) (!TlsFree(k))
|
||||
#define TLSGET(k) TlsGetValue(k)
|
||||
#define TLSSET(k, a) (!TlsSetValue(k, a))
|
||||
#ifdef DEBUG
|
||||
static LPVOID ChkedTlsGetValue(DWORD idx)
|
||||
{
|
||||
LPVOID ret=TlsGetValue(idx);
|
||||
assert(S_OK==GetLastError());
|
||||
return ret;
|
||||
}
|
||||
#undef TLSGET
|
||||
#define TLSGET(k) ChkedTlsGetValue(k)
|
||||
#endif
|
||||
#else
|
||||
#define TLSVAR pthread_key_t
|
||||
#define TLSALLOC(k) pthread_key_create(k, 0)
|
||||
#define TLSFREE(k) pthread_key_delete(k)
|
||||
#define TLSGET(k) pthread_getspecific(k)
|
||||
#define TLSSET(k, a) pthread_setspecific(k, a)
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* Only enable if testing with valgrind. Causes misoperation */
|
||||
#define mspace_malloc(p, s) malloc(s)
|
||||
#define mspace_realloc(p, m, s) realloc(m, s)
|
||||
#define mspace_calloc(p, n, s) calloc(n, s)
|
||||
#define mspace_free(p, m) free(m)
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#if !defined(NO_NED_NAMESPACE)
|
||||
namespace nedalloc {
|
||||
#else
|
||||
extern "C" {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
size_t nedblksize(void *mem) THROWSPEC
|
||||
{
|
||||
#if 0
|
||||
/* Only enable if testing with valgrind. Causes misoperation */
|
||||
return THREADCACHEMAX;
|
||||
#else
|
||||
if(mem)
|
||||
{
|
||||
mchunkptr p=mem2chunk(mem);
|
||||
mstate fm = get_mstate_for(p);
|
||||
assert(ok_magic(fm)); /* If this fails, someone tried to free a block twice */
|
||||
if(ok_magic(fm))
|
||||
return chunksize(p)-overhead_for(p);
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void nedsetvalue(void *v) THROWSPEC { nedpsetvalue(0, v); }
|
||||
void * nedmalloc(size_t size) THROWSPEC { return nedpmalloc(0, size); }
|
||||
void * nedcalloc(size_t no, size_t size) THROWSPEC { return nedpcalloc(0, no, size); }
|
||||
void * nedrealloc(void *mem, size_t size) THROWSPEC { return nedprealloc(0, mem, size); }
|
||||
void nedfree(void *mem) THROWSPEC { nedpfree(0, mem); }
|
||||
void * nedmemalign(size_t alignment, size_t bytes) THROWSPEC { return nedpmemalign(0, alignment, bytes); }
|
||||
#if !NO_MALLINFO
|
||||
struct mallinfo nedmallinfo(void) THROWSPEC { return nedpmallinfo(0); }
|
||||
#endif
|
||||
int nedmallopt(int parno, int value) THROWSPEC { return nedpmallopt(0, parno, value); }
|
||||
int nedmalloc_trim(size_t pad) THROWSPEC { return nedpmalloc_trim(0, pad); }
|
||||
void nedmalloc_stats() THROWSPEC { nedpmalloc_stats(0); }
|
||||
size_t nedmalloc_footprint() THROWSPEC { return nedpmalloc_footprint(0); }
|
||||
void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC { return nedpindependent_calloc(0, elemsno, elemsize, chunks); }
|
||||
void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC { return nedpindependent_comalloc(0, elems, sizes, chunks); }
|
||||
|
||||
struct threadcacheblk_t;
|
||||
typedef struct threadcacheblk_t threadcacheblk;
|
||||
struct threadcacheblk_t
|
||||
{ /* Keep less than 16 bytes on 32 bit systems and 32 bytes on 64 bit systems */
|
||||
#ifdef FULLSANITYCHECKS
|
||||
unsigned int magic;
|
||||
#endif
|
||||
unsigned int lastUsed, size;
|
||||
threadcacheblk *next, *prev;
|
||||
};
|
||||
typedef struct threadcache_t
|
||||
{
|
||||
#ifdef FULLSANITYCHECKS
|
||||
unsigned int magic1;
|
||||
#endif
|
||||
int mymspace; /* Last mspace entry this thread used */
|
||||
long threadid;
|
||||
unsigned int mallocs, frees, successes;
|
||||
size_t freeInCache; /* How much free space is stored in this cache */
|
||||
threadcacheblk *bins[(THREADCACHEMAXBINS+1)*2];
|
||||
#ifdef FULLSANITYCHECKS
|
||||
unsigned int magic2;
|
||||
#endif
|
||||
} threadcache;
|
||||
struct nedpool_t
|
||||
{
|
||||
MLOCK_T mutex;
|
||||
void *uservalue;
|
||||
int threads; /* Max entries in m to use */
|
||||
threadcache *caches[THREADCACHEMAXCACHES];
|
||||
TLSVAR mycache; /* Thread cache for this thread. 0 for unset, negative for use mspace-1 directly, otherwise is cache-1 */
|
||||
mstate m[MAXTHREADSINPOOL+1]; /* mspace entries for this pool */
|
||||
};
|
||||
static nedpool syspool;
|
||||
|
||||
static FORCEINLINE unsigned int size2binidx(size_t _size) THROWSPEC
|
||||
{ /* 8=1000 16=10000 20=10100 24=11000 32=100000 48=110000 4096=1000000000000 */
|
||||
unsigned int topbit, size=(unsigned int)(_size>>4);
|
||||
/* 16=1 20=1 24=1 32=10 48=11 64=100 96=110 128=1000 4096=100000000 */
|
||||
|
||||
#if defined(__GNUC__)
|
||||
topbit = sizeof(size)*__CHAR_BIT__ - 1 - __builtin_clz(size);
|
||||
#elif defined(_MSC_VER) && _MSC_VER>=1300
|
||||
{
|
||||
unsigned long bsrTopBit;
|
||||
|
||||
_BitScanReverse(&bsrTopBit, size);
|
||||
|
||||
topbit = bsrTopBit;
|
||||
}
|
||||
#else
|
||||
#if 0
|
||||
union {
|
||||
unsigned asInt[2];
|
||||
double asDouble;
|
||||
};
|
||||
int n;
|
||||
|
||||
asDouble = (double)size + 0.5;
|
||||
topbit = (asInt[!FOX_BIGENDIAN] >> 20) - 1023;
|
||||
#else
|
||||
{
|
||||
unsigned int x=size;
|
||||
x = x | (x >> 1);
|
||||
x = x | (x >> 2);
|
||||
x = x | (x >> 4);
|
||||
x = x | (x >> 8);
|
||||
x = x | (x >>16);
|
||||
x = ~x;
|
||||
x = x - ((x >> 1) & 0x55555555);
|
||||
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
|
||||
x = (x + (x >> 4)) & 0x0F0F0F0F;
|
||||
x = x + (x << 8);
|
||||
x = x + (x << 16);
|
||||
topbit=31 - (x >> 24);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
return topbit;
|
||||
}
|
||||
|
||||
|
||||
#ifdef FULLSANITYCHECKS
|
||||
static void tcsanitycheck(threadcacheblk **ptr) THROWSPEC
|
||||
{
|
||||
assert((ptr[0] && ptr[1]) || (!ptr[0] && !ptr[1]));
|
||||
if(ptr[0] && ptr[1])
|
||||
{
|
||||
assert(nedblksize(ptr[0])>=sizeof(threadcacheblk));
|
||||
assert(nedblksize(ptr[1])>=sizeof(threadcacheblk));
|
||||
assert(*(unsigned int *) "NEDN"==ptr[0]->magic);
|
||||
assert(*(unsigned int *) "NEDN"==ptr[1]->magic);
|
||||
assert(!ptr[0]->prev);
|
||||
assert(!ptr[1]->next);
|
||||
if(ptr[0]==ptr[1])
|
||||
{
|
||||
assert(!ptr[0]->next);
|
||||
assert(!ptr[1]->prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void tcfullsanitycheck(threadcache *tc) THROWSPEC
|
||||
{
|
||||
threadcacheblk **tcbptr=tc->bins;
|
||||
int n;
|
||||
for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
|
||||
{
|
||||
threadcacheblk *b, *ob=0;
|
||||
tcsanitycheck(tcbptr);
|
||||
for(b=tcbptr[0]; b; ob=b, b=b->next)
|
||||
{
|
||||
assert(*(unsigned int *) "NEDN"==b->magic);
|
||||
assert(!ob || ob->next==b);
|
||||
assert(!ob || b->prev==ob);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static NOINLINE void RemoveCacheEntries(nedpool *p, threadcache *tc, unsigned int age) THROWSPEC
|
||||
{
|
||||
UNREFERENCED_PARAMETER(p);
|
||||
#ifdef FULLSANITYCHECKS
|
||||
tcfullsanitycheck(tc);
|
||||
#endif
|
||||
if(tc->freeInCache)
|
||||
{
|
||||
threadcacheblk **tcbptr=tc->bins;
|
||||
int n;
|
||||
for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
|
||||
{
|
||||
threadcacheblk **tcb=tcbptr+1; /* come from oldest end of list */
|
||||
/*tcsanitycheck(tcbptr);*/
|
||||
for(; *tcb && tc->frees-(*tcb)->lastUsed>=age; )
|
||||
{
|
||||
threadcacheblk *f=*tcb;
|
||||
size_t blksize=f->size; /*nedblksize(f);*/
|
||||
assert(blksize<=nedblksize(f));
|
||||
assert(blksize);
|
||||
#ifdef FULLSANITYCHECKS
|
||||
assert(*(unsigned int *) "NEDN"==(*tcb)->magic);
|
||||
#endif
|
||||
*tcb=(*tcb)->prev;
|
||||
if(*tcb)
|
||||
(*tcb)->next=0;
|
||||
else
|
||||
*tcbptr=0;
|
||||
tc->freeInCache-=blksize;
|
||||
assert((long) tc->freeInCache>=0);
|
||||
mspace_free(0, f);
|
||||
/*tcsanitycheck(tcbptr);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef FULLSANITYCHECKS
|
||||
tcfullsanitycheck(tc);
|
||||
#endif
|
||||
}
|
||||
static void DestroyCaches(nedpool *p) THROWSPEC
|
||||
{
|
||||
if(p->caches)
|
||||
{
|
||||
threadcache *tc;
|
||||
int n;
|
||||
for(n=0; n<THREADCACHEMAXCACHES; n++)
|
||||
{
|
||||
if((tc=p->caches[n]))
|
||||
{
|
||||
tc->frees++;
|
||||
RemoveCacheEntries(p, tc, 0);
|
||||
assert(!tc->freeInCache);
|
||||
tc->mymspace=-1;
|
||||
tc->threadid=0;
|
||||
mspace_free(0, tc);
|
||||
p->caches[n]=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static NOINLINE threadcache *AllocCache(nedpool *p) THROWSPEC
|
||||
{
|
||||
threadcache *tc=0;
|
||||
int n, end;
|
||||
ACQUIRE_LOCK(&p->mutex);
|
||||
for(n=0; n<THREADCACHEMAXCACHES && p->caches[n]; n++);
|
||||
if(THREADCACHEMAXCACHES==n)
|
||||
{ /* List exhausted, so disable for this thread */
|
||||
RELEASE_LOCK(&p->mutex);
|
||||
return 0;
|
||||
}
|
||||
tc=p->caches[n]=(threadcache *) mspace_calloc(p->m[0], 1, sizeof(threadcache));
|
||||
if(!tc)
|
||||
{
|
||||
RELEASE_LOCK(&p->mutex);
|
||||
return 0;
|
||||
}
|
||||
#ifdef FULLSANITYCHECKS
|
||||
tc->magic1=*(unsigned int *)"NEDMALC1";
|
||||
tc->magic2=*(unsigned int *)"NEDMALC2";
|
||||
#endif
|
||||
tc->threadid=(long)(size_t)CURRENT_THREAD;
|
||||
for(end=0; p->m[end]; end++);
|
||||
tc->mymspace=abs(tc->threadid) % end;
|
||||
RELEASE_LOCK(&p->mutex);
|
||||
if(TLSSET(p->mycache, (void *)(size_t)(n+1))) abort();
|
||||
return tc;
|
||||
}
|
||||
|
||||
static void *threadcache_malloc(nedpool *p, threadcache *tc, size_t *size) THROWSPEC
|
||||
{
|
||||
void *ret=0;
|
||||
unsigned int bestsize;
|
||||
unsigned int idx=size2binidx(*size);
|
||||
size_t blksize=0;
|
||||
threadcacheblk *blk, **binsptr;
|
||||
UNREFERENCED_PARAMETER(p);
|
||||
#ifdef FULLSANITYCHECKS
|
||||
tcfullsanitycheck(tc);
|
||||
#endif
|
||||
/* Calculate best fit bin size */
|
||||
bestsize=1<<(idx+4);
|
||||
#if 0
|
||||
/* Finer grained bin fit */
|
||||
idx<<=1;
|
||||
if(*size>bestsize)
|
||||
{
|
||||
idx++;
|
||||
bestsize+=bestsize>>1;
|
||||
}
|
||||
if(*size>bestsize)
|
||||
{
|
||||
idx++;
|
||||
bestsize=1<<(4+(idx>>1));
|
||||
}
|
||||
#else
|
||||
if(*size>bestsize)
|
||||
{
|
||||
idx++;
|
||||
bestsize<<=1;
|
||||
}
|
||||
#endif
|
||||
assert(bestsize>=*size);
|
||||
if(*size<bestsize) *size=bestsize;
|
||||
assert(*size<=THREADCACHEMAX);
|
||||
assert(idx<=THREADCACHEMAXBINS);
|
||||
binsptr=&tc->bins[idx*2];
|
||||
/* Try to match close, but move up a bin if necessary */
|
||||
blk=*binsptr;
|
||||
if(!blk || blk->size<*size)
|
||||
{ /* Bump it up a bin */
|
||||
if(idx<THREADCACHEMAXBINS)
|
||||
{
|
||||
idx++;
|
||||
binsptr+=2;
|
||||
blk=*binsptr;
|
||||
}
|
||||
}
|
||||
if(blk)
|
||||
{
|
||||
blksize=blk->size; /*nedblksize(blk);*/
|
||||
assert(nedblksize(blk)>=blksize);
|
||||
assert(blksize>=*size);
|
||||
if(blk->next)
|
||||
blk->next->prev=0;
|
||||
*binsptr=blk->next;
|
||||
if(!*binsptr)
|
||||
binsptr[1]=0;
|
||||
#ifdef FULLSANITYCHECKS
|
||||
blk->magic=0;
|
||||
#endif
|
||||
assert(binsptr[0]!=blk && binsptr[1]!=blk);
|
||||
assert(nedblksize(blk)>=sizeof(threadcacheblk) && nedblksize(blk)<=THREADCACHEMAX+CHUNK_OVERHEAD);
|
||||
/*printf("malloc: %p, %p, %p, %lu\n", p, tc, blk, (long) size);*/
|
||||
ret=(void *) blk;
|
||||
}
|
||||
++tc->mallocs;
|
||||
if(ret)
|
||||
{
|
||||
assert(blksize>=*size);
|
||||
++tc->successes;
|
||||
tc->freeInCache-=blksize;
|
||||
assert((long) tc->freeInCache>=0);
|
||||
}
|
||||
#if defined(DEBUG) && 0
|
||||
if(!(tc->mallocs & 0xfff))
|
||||
{
|
||||
printf("*** threadcache=%u, mallocs=%u (%f), free=%u (%f), freeInCache=%u\n", (unsigned int) tc->threadid, tc->mallocs,
|
||||
(float) tc->successes/tc->mallocs, tc->frees, (float) tc->successes/tc->frees, (unsigned int) tc->freeInCache);
|
||||
}
|
||||
#endif
|
||||
#ifdef FULLSANITYCHECKS
|
||||
tcfullsanitycheck(tc);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
static NOINLINE void ReleaseFreeInCache(nedpool *p, threadcache *tc, int mymspace) THROWSPEC
|
||||
{
|
||||
unsigned int age=THREADCACHEMAXFREESPACE/8192;
|
||||
/*ACQUIRE_LOCK(&p->m[mymspace]->mutex);*/
|
||||
UNREFERENCED_PARAMETER(mymspace);
|
||||
while(age && tc->freeInCache>=THREADCACHEMAXFREESPACE)
|
||||
{
|
||||
RemoveCacheEntries(p, tc, age);
|
||||
/*printf("*** Removing cache entries older than %u (%u)\n", age, (unsigned int) tc->freeInCache);*/
|
||||
age>>=1;
|
||||
}
|
||||
/*RELEASE_LOCK(&p->m[mymspace]->mutex);*/
|
||||
}
|
||||
static void threadcache_free(nedpool *p, threadcache *tc, int mymspace, void *mem, size_t size) THROWSPEC
|
||||
{
|
||||
unsigned int bestsize;
|
||||
unsigned int idx=size2binidx(size);
|
||||
threadcacheblk **binsptr, *tck=(threadcacheblk *) mem;
|
||||
assert(size>=sizeof(threadcacheblk) && size<=THREADCACHEMAX+CHUNK_OVERHEAD);
|
||||
#ifdef DEBUG
|
||||
{ /* Make sure this is a valid memory block */
|
||||
mchunkptr p = mem2chunk(mem);
|
||||
mstate fm = get_mstate_for(p);
|
||||
if (!ok_magic(fm)) {
|
||||
USAGE_ERROR_ACTION(fm, p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef FULLSANITYCHECKS
|
||||
tcfullsanitycheck(tc);
|
||||
#endif
|
||||
/* Calculate best fit bin size */
|
||||
bestsize=1<<(idx+4);
|
||||
#if 0
|
||||
/* Finer grained bin fit */
|
||||
idx<<=1;
|
||||
if(size>bestsize)
|
||||
{
|
||||
unsigned int biggerbestsize=bestsize+bestsize<<1;
|
||||
if(size>=biggerbestsize)
|
||||
{
|
||||
idx++;
|
||||
bestsize=biggerbestsize;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(bestsize!=size) /* dlmalloc can round up, so we round down to preserve indexing */
|
||||
size=bestsize;
|
||||
binsptr=&tc->bins[idx*2];
|
||||
assert(idx<=THREADCACHEMAXBINS);
|
||||
if(tck==*binsptr)
|
||||
{
|
||||
fprintf(stderr, "Attempt to free already freed memory block %p - aborting!\n", tck);
|
||||
abort();
|
||||
}
|
||||
#ifdef FULLSANITYCHECKS
|
||||
tck->magic=*(unsigned int *) "NEDN";
|
||||
#endif
|
||||
tck->lastUsed=++tc->frees;
|
||||
tck->size=(unsigned int) size;
|
||||
tck->next=*binsptr;
|
||||
tck->prev=0;
|
||||
if(tck->next)
|
||||
tck->next->prev=tck;
|
||||
else
|
||||
binsptr[1]=tck;
|
||||
assert(!*binsptr || (*binsptr)->size==tck->size);
|
||||
*binsptr=tck;
|
||||
assert(tck==tc->bins[idx*2]);
|
||||
assert(tc->bins[idx*2+1]==tck || binsptr[0]->next->prev==tck);
|
||||
/*printf("free: %p, %p, %p, %lu\n", p, tc, mem, (long) size);*/
|
||||
tc->freeInCache+=size;
|
||||
#ifdef FULLSANITYCHECKS
|
||||
tcfullsanitycheck(tc);
|
||||
#endif
|
||||
#if 1
|
||||
if(tc->freeInCache>=THREADCACHEMAXFREESPACE)
|
||||
ReleaseFreeInCache(p, tc, mymspace);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static NOINLINE int InitPool(nedpool *p, size_t capacity, int threads) THROWSPEC
|
||||
{ /* threads is -1 for system pool */
|
||||
ensure_initialization();
|
||||
ACQUIRE_MALLOC_GLOBAL_LOCK();
|
||||
if(p->threads) goto done;
|
||||
if(INITIAL_LOCK(&p->mutex)) goto err;
|
||||
if(TLSALLOC(&p->mycache)) goto err;
|
||||
if(!(p->m[0]=(mstate) create_mspace(capacity, 1))) goto err;
|
||||
p->m[0]->extp=p;
|
||||
p->threads=(threads<1 || threads>MAXTHREADSINPOOL) ? MAXTHREADSINPOOL : threads;
|
||||
done:
|
||||
RELEASE_MALLOC_GLOBAL_LOCK();
|
||||
return 1;
|
||||
err:
|
||||
if(threads<0)
|
||||
abort(); /* If you can't allocate for system pool, we're screwed */
|
||||
DestroyCaches(p);
|
||||
if(p->m[0])
|
||||
{
|
||||
destroy_mspace(p->m[0]);
|
||||
p->m[0]=0;
|
||||
}
|
||||
if(p->mycache)
|
||||
{
|
||||
if(TLSFREE(p->mycache)) abort();
|
||||
p->mycache=0;
|
||||
}
|
||||
RELEASE_MALLOC_GLOBAL_LOCK();
|
||||
return 0;
|
||||
}
|
||||
static NOINLINE mstate FindMSpace(nedpool *p, threadcache *tc, int *lastUsed, size_t size) THROWSPEC
|
||||
{ /* Gets called when thread's last used mspace is in use. The strategy
|
||||
is to run through the list of all available mspaces looking for an
|
||||
unlocked one and if we fail, we create a new one so long as we don't
|
||||
exceed p->threads */
|
||||
int n, end;
|
||||
for(n=end=*lastUsed+1; p->m[n]; end=++n)
|
||||
{
|
||||
if(TRY_LOCK(&p->m[n]->mutex)) goto found;
|
||||
}
|
||||
for(n=0; n<*lastUsed && p->m[n]; n++)
|
||||
{
|
||||
if(TRY_LOCK(&p->m[n]->mutex)) goto found;
|
||||
}
|
||||
if(end<p->threads)
|
||||
{
|
||||
mstate temp;
|
||||
if(!(temp=(mstate) create_mspace(size, 1)))
|
||||
goto badexit;
|
||||
/* Now we're ready to modify the lists, we lock */
|
||||
ACQUIRE_LOCK(&p->mutex);
|
||||
while(p->m[end] && end<p->threads)
|
||||
end++;
|
||||
if(end>=p->threads)
|
||||
{ /* Drat, must destroy it now */
|
||||
RELEASE_LOCK(&p->mutex);
|
||||
destroy_mspace((mspace) temp);
|
||||
goto badexit;
|
||||
}
|
||||
/* We really want to make sure this goes into memory now but we
|
||||
have to be careful of breaking aliasing rules, so write it twice */
|
||||
*((volatile struct malloc_state **) &p->m[end])=p->m[end]=temp;
|
||||
ACQUIRE_LOCK(&p->m[end]->mutex);
|
||||
/*printf("Created mspace idx %d\n", end);*/
|
||||
RELEASE_LOCK(&p->mutex);
|
||||
n=end;
|
||||
goto found;
|
||||
}
|
||||
/* Let it lock on the last one it used */
|
||||
badexit:
|
||||
ACQUIRE_LOCK(&p->m[*lastUsed]->mutex);
|
||||
return p->m[*lastUsed];
|
||||
found:
|
||||
*lastUsed=n;
|
||||
if(tc)
|
||||
tc->mymspace=n;
|
||||
else
|
||||
{
|
||||
if(TLSSET(p->mycache, (void *)(size_t)(-(n+1)))) abort();
|
||||
}
|
||||
return p->m[n];
|
||||
}
|
||||
|
||||
nedpool *nedcreatepool(size_t capacity, int threads) THROWSPEC
|
||||
{
|
||||
nedpool *ret;
|
||||
if(!(ret=(nedpool *) nedpcalloc(0, 1, sizeof(nedpool)))) return 0;
|
||||
if(!InitPool(ret, capacity, threads))
|
||||
{
|
||||
nedpfree(0, ret);
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void neddestroypool(nedpool *p) THROWSPEC
|
||||
{
|
||||
int n;
|
||||
ACQUIRE_LOCK(&p->mutex);
|
||||
DestroyCaches(p);
|
||||
for(n=0; p->m[n]; n++)
|
||||
{
|
||||
destroy_mspace(p->m[n]);
|
||||
p->m[n]=0;
|
||||
}
|
||||
RELEASE_LOCK(&p->mutex);
|
||||
if(TLSFREE(p->mycache)) abort();
|
||||
nedpfree(0, p);
|
||||
}
|
||||
void neddestroysyspool() THROWSPEC
|
||||
{
|
||||
nedpool *p=&syspool;
|
||||
int n;
|
||||
ACQUIRE_LOCK(&p->mutex);
|
||||
DestroyCaches(p);
|
||||
for(n=0; p->m[n]; n++)
|
||||
{
|
||||
destroy_mspace(p->m[n]);
|
||||
p->m[n]=0;
|
||||
}
|
||||
/* Render syspool unusable */
|
||||
for(n=0; n<THREADCACHEMAXCACHES; n++)
|
||||
p->caches[n]=(threadcache *) 0xdeadbeef;
|
||||
for(n=0; n<MAXTHREADSINPOOL+1; n++)
|
||||
p->m[n]=(mstate) 0xdeadbeef;
|
||||
if(TLSFREE(p->mycache)) abort();
|
||||
RELEASE_LOCK(&p->mutex);
|
||||
}
|
||||
|
||||
void nedpsetvalue(nedpool *p, void *v) THROWSPEC
|
||||
{
|
||||
if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
|
||||
p->uservalue=v;
|
||||
}
|
||||
void *nedgetvalue(nedpool **p, void *mem) THROWSPEC
|
||||
{
|
||||
nedpool *np=0;
|
||||
mchunkptr mcp=mem2chunk(mem);
|
||||
mstate fm;
|
||||
if(!(is_aligned(chunk2mem(mcp))) && mcp->head != FENCEPOST_HEAD) return 0;
|
||||
if(!cinuse(mcp)) return 0;
|
||||
if(!next_pinuse(mcp)) return 0;
|
||||
if(!is_mmapped(mcp) && !pinuse(mcp))
|
||||
{
|
||||
if(next_chunk(prev_chunk(mcp))!=mcp) return 0;
|
||||
}
|
||||
fm=get_mstate_for(mcp);
|
||||
if(!ok_magic(fm)) return 0;
|
||||
if(!ok_address(fm, mcp)) return 0;
|
||||
if(!fm->extp) return 0;
|
||||
np=(nedpool *) fm->extp;
|
||||
if(p) *p=np;
|
||||
return np->uservalue;
|
||||
}
|
||||
|
||||
void neddisablethreadcache(nedpool *p) THROWSPEC
|
||||
{
|
||||
int mycache;
|
||||
if(!p)
|
||||
{
|
||||
p=&syspool;
|
||||
if(!syspool.threads) InitPool(&syspool, 0, -1);
|
||||
}
|
||||
mycache=(int)(size_t) TLSGET(p->mycache);
|
||||
if(!mycache)
|
||||
{ /* Set to mspace 0 */
|
||||
if(TLSSET(p->mycache, (void *)-1)) abort();
|
||||
}
|
||||
else if(mycache>0)
|
||||
{ /* Set to last used mspace */
|
||||
threadcache *tc=p->caches[mycache-1];
|
||||
#if defined(DEBUG)
|
||||
printf("Threadcache utilisation: %lf%% in cache with %lf%% lost to other threads\n",
|
||||
100.0*tc->successes/tc->mallocs, 100.0*((double) tc->mallocs-tc->frees)/tc->mallocs);
|
||||
#endif
|
||||
if(TLSSET(p->mycache, (void *)(size_t)(-tc->mymspace))) abort();
|
||||
tc->frees++;
|
||||
RemoveCacheEntries(p, tc, 0);
|
||||
assert(!tc->freeInCache);
|
||||
tc->mymspace=-1;
|
||||
tc->threadid=0;
|
||||
mspace_free(0, p->caches[mycache-1]);
|
||||
p->caches[mycache-1]=0;
|
||||
}
|
||||
}
|
||||
|
||||
#define GETMSPACE(m,p,tc,ms,s,action) \
|
||||
do \
|
||||
{ \
|
||||
mstate m = GetMSpace((p),(tc),(ms),(s)); \
|
||||
action; \
|
||||
RELEASE_LOCK(&m->mutex); \
|
||||
} while (0)
|
||||
|
||||
static FORCEINLINE mstate GetMSpace(nedpool *p, threadcache *tc, int mymspace, size_t size) THROWSPEC
|
||||
{ /* Returns a locked and ready for use mspace */
|
||||
mstate m=p->m[mymspace];
|
||||
assert(m);
|
||||
if(!TRY_LOCK(&p->m[mymspace]->mutex)) m=FindMSpace(p, tc, &mymspace, size);\
|
||||
/*assert(IS_LOCKED(&p->m[mymspace]->mutex));*/
|
||||
return m;
|
||||
}
|
||||
static FORCEINLINE void GetThreadCache(nedpool **p, threadcache **tc, int *mymspace, size_t *size) THROWSPEC
|
||||
{
|
||||
int mycache;
|
||||
if(size && *size<sizeof(threadcacheblk)) *size=sizeof(threadcacheblk);
|
||||
if(!*p)
|
||||
{
|
||||
*p=&syspool;
|
||||
if(!syspool.threads) InitPool(&syspool, 0, -1);
|
||||
}
|
||||
mycache=(int)(size_t) TLSGET((*p)->mycache);
|
||||
if(mycache>0)
|
||||
{
|
||||
*tc=(*p)->caches[mycache-1];
|
||||
*mymspace=(*tc)->mymspace;
|
||||
}
|
||||
else if(!mycache)
|
||||
{
|
||||
*tc=AllocCache(*p);
|
||||
if(!*tc)
|
||||
{ /* Disable */
|
||||
if(TLSSET((*p)->mycache, (void *)-1)) abort();
|
||||
*mymspace=0;
|
||||
}
|
||||
else
|
||||
*mymspace=(*tc)->mymspace;
|
||||
}
|
||||
else
|
||||
{
|
||||
*tc=0;
|
||||
*mymspace=-mycache-1;
|
||||
}
|
||||
assert(*mymspace>=0);
|
||||
assert((long)(size_t)CURRENT_THREAD==(*tc)->threadid);
|
||||
#ifdef FULLSANITYCHECKS
|
||||
if(*tc)
|
||||
{
|
||||
if(*(unsigned int *)"NEDMALC1"!=(*tc)->magic1 || *(unsigned int *)"NEDMALC2"!=(*tc)->magic2)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void * nedpmalloc(nedpool *p, size_t size) THROWSPEC
|
||||
{
|
||||
void *ret=0;
|
||||
threadcache *tc;
|
||||
int mymspace;
|
||||
GetThreadCache(&p, &tc, &mymspace, &size);
|
||||
#if THREADCACHEMAX
|
||||
if(tc && size<=THREADCACHEMAX)
|
||||
{ /* Use the thread cache */
|
||||
ret=threadcache_malloc(p, tc, &size);
|
||||
}
|
||||
#endif
|
||||
if(!ret)
|
||||
{ /* Use this thread's mspace */
|
||||
GETMSPACE(m, p, tc, mymspace, size,
|
||||
ret=mspace_malloc(m, size));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void * nedpcalloc(nedpool *p, size_t no, size_t size) THROWSPEC
|
||||
{
|
||||
size_t rsize=size*no;
|
||||
void *ret=0;
|
||||
threadcache *tc;
|
||||
int mymspace;
|
||||
GetThreadCache(&p, &tc, &mymspace, &rsize);
|
||||
#if THREADCACHEMAX
|
||||
if(tc && rsize<=THREADCACHEMAX)
|
||||
{ /* Use the thread cache */
|
||||
if((ret=threadcache_malloc(p, tc, &rsize)))
|
||||
memset(ret, 0, rsize);
|
||||
}
|
||||
#endif
|
||||
if(!ret)
|
||||
{ /* Use this thread's mspace */
|
||||
GETMSPACE(m, p, tc, mymspace, rsize,
|
||||
ret=mspace_calloc(m, 1, rsize));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void * nedprealloc(nedpool *p, void *mem, size_t size) THROWSPEC
|
||||
{
|
||||
void *ret=0;
|
||||
threadcache *tc;
|
||||
int mymspace;
|
||||
if(!mem) return nedpmalloc(p, size);
|
||||
GetThreadCache(&p, &tc, &mymspace, &size);
|
||||
#if THREADCACHEMAX
|
||||
if(tc && size && size<=THREADCACHEMAX)
|
||||
{ /* Use the thread cache */
|
||||
size_t memsize=nedblksize(mem);
|
||||
assert(memsize);
|
||||
if((ret=threadcache_malloc(p, tc, &size)))
|
||||
{
|
||||
memcpy(ret, mem, memsize<size ? memsize : size);
|
||||
if(memsize<=THREADCACHEMAX)
|
||||
threadcache_free(p, tc, mymspace, mem, memsize);
|
||||
else
|
||||
mspace_free(0, mem);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(!ret)
|
||||
{ /* Reallocs always happen in the mspace they happened in, so skip
|
||||
locking the preferred mspace for this thread */
|
||||
ret=mspace_realloc(0, mem, size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void nedpfree(nedpool *p, void *mem) THROWSPEC
|
||||
{ /* Frees always happen in the mspace they happened in, so skip
|
||||
locking the preferred mspace for this thread */
|
||||
threadcache *tc;
|
||||
int mymspace;
|
||||
size_t memsize;
|
||||
assert(mem);
|
||||
GetThreadCache(&p, &tc, &mymspace, 0);
|
||||
#if THREADCACHEMAX
|
||||
memsize=nedblksize(mem);
|
||||
assert(memsize);
|
||||
if(mem && tc && memsize<=(THREADCACHEMAX+CHUNK_OVERHEAD))
|
||||
threadcache_free(p, tc, mymspace, mem, memsize);
|
||||
else
|
||||
#endif
|
||||
mspace_free(0, mem);
|
||||
}
|
||||
void * nedpmemalign(nedpool *p, size_t alignment, size_t bytes) THROWSPEC
|
||||
{
|
||||
void *ret;
|
||||
threadcache *tc;
|
||||
int mymspace;
|
||||
GetThreadCache(&p, &tc, &mymspace, &bytes);
|
||||
{ /* Use this thread's mspace */
|
||||
GETMSPACE(m, p, tc, mymspace, bytes,
|
||||
ret=mspace_memalign(m, alignment, bytes));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#if !NO_MALLINFO
|
||||
struct mallinfo nedpmallinfo(nedpool *p) THROWSPEC
|
||||
{
|
||||
int n;
|
||||
struct mallinfo ret ={0,0,0,0,0,0,0,0,0,0};
|
||||
if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
|
||||
for(n=0; p->m[n]; n++)
|
||||
{
|
||||
struct mallinfo t=mspace_mallinfo(p->m[n]);
|
||||
ret.arena+=t.arena;
|
||||
ret.ordblks+=t.ordblks;
|
||||
ret.hblkhd+=t.hblkhd;
|
||||
ret.usmblks+=t.usmblks;
|
||||
ret.uordblks+=t.uordblks;
|
||||
ret.fordblks+=t.fordblks;
|
||||
ret.keepcost+=t.keepcost;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
int nedpmallopt(nedpool *p, int parno, int value) THROWSPEC
|
||||
{
|
||||
UNREFERENCED_PARAMETER(p);
|
||||
return mspace_mallopt(parno, value);
|
||||
}
|
||||
int nedpmalloc_trim(nedpool *p, size_t pad) THROWSPEC
|
||||
{
|
||||
int n, ret=0;
|
||||
if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
|
||||
for(n=0; p->m[n]; n++)
|
||||
{
|
||||
ret+=mspace_trim(p->m[n], pad);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void nedpmalloc_stats(nedpool *p) THROWSPEC
|
||||
{
|
||||
int n;
|
||||
if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
|
||||
for(n=0; p->m[n]; n++)
|
||||
{
|
||||
mspace_malloc_stats(p->m[n]);
|
||||
}
|
||||
}
|
||||
size_t nedpmalloc_footprint(nedpool *p) THROWSPEC
|
||||
{
|
||||
size_t ret=0;
|
||||
int n;
|
||||
if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
|
||||
for(n=0; p->m[n]; n++)
|
||||
{
|
||||
ret+=mspace_footprint(p->m[n]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void **nedpindependent_calloc(nedpool *p, size_t elemsno, size_t elemsize, void **chunks) THROWSPEC
|
||||
{
|
||||
void **ret;
|
||||
threadcache *tc;
|
||||
int mymspace;
|
||||
GetThreadCache(&p, &tc, &mymspace, &elemsize);
|
||||
GETMSPACE(m, p, tc, mymspace, elemsno*elemsize,
|
||||
ret=mspace_independent_calloc(m, elemsno, elemsize, chunks));
|
||||
return ret;
|
||||
}
|
||||
void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **chunks) THROWSPEC
|
||||
{
|
||||
void **ret;
|
||||
threadcache *tc;
|
||||
int mymspace;
|
||||
size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
|
||||
if(!adjustedsizes) return 0;
|
||||
for(i=0; i<elems; i++)
|
||||
adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
|
||||
GetThreadCache(&p, &tc, &mymspace, 0);
|
||||
GETMSPACE(m, p, tc, mymspace, 0,
|
||||
ret=mspace_independent_comalloc(m, elems, adjustedsizes, chunks));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
Loading…
Reference in a new issue