970 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			970 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
| /* 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"
 | |
| #if defined(WIN32)
 | |
|  #include <malloc.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 */
 | |
| #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
 | |
| 
 | |
| /* 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);
 | |
| 		assert(cinuse(p));	/* If this fails, someone tried to free a block twice */
 | |
| 		if(cinuse(p))
 | |
| 			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(void) THROWSPEC					{ nedpmalloc_stats(0); }
 | |
| size_t nedmalloc_footprint(void) 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
 | |
| {
 | |
| #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=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;
 | |
| #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);*/
 | |
| 	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 **_m=(volatile struct malloc_state **) &p->m[end];
 | |
| 			*_m=(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 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};
 | |
| 	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
 | |
| {
 | |
| 	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;
 | |
| }
 | |
| 
 | |
| #ifdef OVERRIDE_STRDUP
 | |
| /*
 | |
|  * This implementation is purely there to override the libc version, to
 | |
|  * avoid a crash due to allocation and free on different 'heaps'.
 | |
|  */
 | |
| char *strdup(const char *s1)
 | |
| {
 | |
| 	char *s2 = 0;
 | |
| 	if (s1) {
 | |
| 		s2 = malloc(strlen(s1) + 1);
 | |
| 		strcpy(s2, s1);
 | |
| 	}
 | |
| 	return s2;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #if defined(__cplusplus)
 | |
| }
 | |
| #endif
 |