diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.c | 12 | ||||
-rw-r--r-- | src/mem3.c | 508 | ||||
-rw-r--r-- | src/sqlite.h.in | 3 | ||||
-rw-r--r-- | src/test_malloc.c | 35 |
4 files changed, 306 insertions, 252 deletions
diff --git a/src/main.c b/src/main.c index 4659b634f..e5f6f6ee2 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.458 2008/06/23 14:15:53 danielk1977 Exp $ +** $Id: main.c,v 1.459 2008/06/24 19:02:55 danielk1977 Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -149,13 +149,21 @@ int sqlite3_config(int op, ...){ sqlite3Config.bFullMutex = 1; break; } +#ifdef SQLITE_ENABLE_MEMPOOL + case SQLITE_CONFIG_MEMPOOL: { + u8 *pMem = va_arg(ap, u8*); + int nMem = va_arg(ap, int); + rc = sqlite3MemSetMempool(pMem, nMem); + break; + } +#endif case SQLITE_CONFIG_MALLOC: { /* Specify an alternative malloc implementation */ sqlite3Config.m = *va_arg(ap, sqlite3_mem_methods*); break; } case SQLITE_CONFIG_GETMALLOC: { - /* Specify an alternative malloc implementation */ + /* Retrieve the current malloc() implementation */ if( sqlite3Config.m.xMalloc==0 ) sqlite3MemSetDefault(); *va_arg(ap, sqlite3_mem_methods*) = sqlite3Config.m; break; diff --git a/src/mem3.c b/src/mem3.c index fc263ee22..ef700f7ee 100644 --- a/src/mem3.c +++ b/src/mem3.c @@ -14,21 +14,24 @@ ** ** This version of the memory allocation subsystem omits all ** use of malloc(). All dynamically allocatable memory is -** contained in a static array, mem.aPool[]. The size of this +** contained in a static array, mem3.aPool[]. The size of this ** fixed memory pool is SQLITE_MEMORY_SIZE bytes. ** ** This version of the memory allocation subsystem is used if ** and only if SQLITE_MEMORY_SIZE is defined. ** -** $Id: mem3.c,v 1.14 2008/06/18 17:09:10 danielk1977 Exp $ +** $Id: mem3.c,v 1.15 2008/06/24 19:02:55 danielk1977 Exp $ */ #include "sqliteInt.h" /* -** This version of the memory allocator is used only when -** SQLITE_MEMORY_SIZE is defined. +** This version of the memory allocator is only built into the library +** SQLITE_ENABLE_MEMPOOL is defined. Defining this symbol does not +** mean that the library will use a memory-pool by default, just that +** it is available. The mempool allocator is activated by calling +** sqlite3_config(). */ -#ifdef SQLITE_MEMORY_SIZE +#ifdef SQLITE_ENABLE_MEMPOOL /* ** Maximum size (in Mem3Blocks) of a "small" chunk. @@ -58,7 +61,7 @@ ** u.hdr.prevSize can be part of the data for that chunk and should ** not be read or written. ** -** We often identify a chunk by its index in mem.aPool[]. When +** We often identify a chunk by its index in mem3.aPool[]. When ** this is done, the chunk index refers to the second block of ** the chunk. In this way, the first chunk has an index of 1. ** A chunk index of 0 means "no such chunk" and is the equivalent @@ -66,8 +69,8 @@ ** ** The second block of free chunks is of the form u.list. The ** two fields form a double-linked list of chunks of related sizes. -** Pointers to the head of the list are stored in mem.aiSmall[] -** for smaller chunks and mem.aiHash[] for larger chunks. +** Pointers to the head of the list are stored in mem3.aiSmall[] +** for smaller chunks and mem3.aiHash[] for larger chunks. ** ** The second block of a chunk is user data if the chunk is checked ** out. If a chunk is checked out, the user data may extend into @@ -81,15 +84,15 @@ struct Mem3Block { u32 size4x; /* 4x the size of current chunk in Mem3Block elements */ } hdr; struct { - u32 next; /* Index in mem.aPool[] of next free chunk */ - u32 prev; /* Index in mem.aPool[] of previous free chunk */ + u32 next; /* Index in mem3.aPool[] of next free chunk */ + u32 prev; /* Index in mem3.aPool[] of previous free chunk */ } list; } u; }; /* ** All of the static variables used by this module are collected -** into a single structure named "mem". This is to keep the +** into a single structure named "mem3". This is to keep the ** static variables organized and to reduce namespace pollution ** when this module is combined with other in the amalgamation. */ @@ -127,29 +130,32 @@ static struct { u32 aiHash[N_HASH]; /* For sizes MX_SMALL+1 and larger */ /* - ** Memory available for allocation + ** Memory available for allocation. nPool is the size of the array + ** (in Mem3Blocks) pointed to by aPool less 2. */ - Mem3Block aPool[SQLITE_MEMORY_SIZE/sizeof(Mem3Block)+2]; -} mem; + u32 nPool; + Mem3Block *aPool; + /* Mem3Block aPool[SQLITE_MEMORY_SIZE/sizeof(Mem3Block)+2]; */ +} mem3; /* -** Unlink the chunk at mem.aPool[i] from list it is currently +** Unlink the chunk at mem3.aPool[i] from list it is currently ** on. *pRoot is the list that i is a member of. */ static void memsys3UnlinkFromList(u32 i, u32 *pRoot){ - u32 next = mem.aPool[i].u.list.next; - u32 prev = mem.aPool[i].u.list.prev; - assert( sqlite3_mutex_held(mem.mutex) ); + u32 next = mem3.aPool[i].u.list.next; + u32 prev = mem3.aPool[i].u.list.prev; + assert( sqlite3_mutex_held(mem3.mutex) ); if( prev==0 ){ *pRoot = next; }else{ - mem.aPool[prev].u.list.next = next; + mem3.aPool[prev].u.list.next = next; } if( next ){ - mem.aPool[next].u.list.prev = prev; + mem3.aPool[next].u.list.prev = prev; } - mem.aPool[i].u.list.next = 0; - mem.aPool[i].u.list.prev = 0; + mem3.aPool[i].u.list.next = 0; + mem3.aPool[i].u.list.prev = 0; } /* @@ -158,30 +164,30 @@ static void memsys3UnlinkFromList(u32 i, u32 *pRoot){ */ static void memsys3Unlink(u32 i){ u32 size, hash; - assert( sqlite3_mutex_held(mem.mutex) ); - assert( (mem.aPool[i-1].u.hdr.size4x & 1)==0 ); + assert( sqlite3_mutex_held(mem3.mutex) ); + assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 ); assert( i>=1 ); - size = mem.aPool[i-1].u.hdr.size4x/4; - assert( size==mem.aPool[i+size-1].u.hdr.prevSize ); + size = mem3.aPool[i-1].u.hdr.size4x/4; + assert( size==mem3.aPool[i+size-1].u.hdr.prevSize ); assert( size>=2 ); if( size <= MX_SMALL ){ - memsys3UnlinkFromList(i, &mem.aiSmall[size-2]); + memsys3UnlinkFromList(i, &mem3.aiSmall[size-2]); }else{ hash = size % N_HASH; - memsys3UnlinkFromList(i, &mem.aiHash[hash]); + memsys3UnlinkFromList(i, &mem3.aiHash[hash]); } } /* -** Link the chunk at mem.aPool[i] so that is on the list rooted +** Link the chunk at mem3.aPool[i] so that is on the list rooted ** at *pRoot. */ static void memsys3LinkIntoList(u32 i, u32 *pRoot){ - assert( sqlite3_mutex_held(mem.mutex) ); - mem.aPool[i].u.list.next = *pRoot; - mem.aPool[i].u.list.prev = 0; + assert( sqlite3_mutex_held(mem3.mutex) ); + mem3.aPool[i].u.list.next = *pRoot; + mem3.aPool[i].u.list.prev = 0; if( *pRoot ){ - mem.aPool[*pRoot].u.list.prev = i; + mem3.aPool[*pRoot].u.list.prev = i; } *pRoot = i; } @@ -192,117 +198,51 @@ static void memsys3LinkIntoList(u32 i, u32 *pRoot){ */ static void memsys3Link(u32 i){ u32 size, hash; - assert( sqlite3_mutex_held(mem.mutex) ); + assert( sqlite3_mutex_held(mem3.mutex) ); assert( i>=1 ); - assert( (mem.aPool[i-1].u.hdr.size4x & 1)==0 ); - size = mem.aPool[i-1].u.hdr.size4x/4; - assert( size==mem.aPool[i+size-1].u.hdr.prevSize ); + assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 ); + size = mem3.aPool[i-1].u.hdr.size4x/4; + assert( size==mem3.aPool[i+size-1].u.hdr.prevSize ); assert( size>=2 ); if( size <= MX_SMALL ){ - memsys3LinkIntoList(i, &mem.aiSmall[size-2]); + memsys3LinkIntoList(i, &mem3.aiSmall[size-2]); }else{ hash = size % N_HASH; - memsys3LinkIntoList(i, &mem.aiHash[hash]); + memsys3LinkIntoList(i, &mem3.aiHash[hash]); } } /* -** Enter the mutex mem.mutex. Allocate it if it is not already allocated. +** Enter the mutex mem3.mutex. Allocate it if it is not already allocated. ** ** Also: Initialize the memory allocation subsystem the first time ** this routine is called. */ static void memsys3Enter(void){ - if( mem.mutex==0 ){ - mem.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); - mem.aPool[0].u.hdr.size4x = SQLITE_MEMORY_SIZE/2 + 2; - mem.aPool[SQLITE_MEMORY_SIZE/8].u.hdr.prevSize = SQLITE_MEMORY_SIZE/8; - mem.aPool[SQLITE_MEMORY_SIZE/8].u.hdr.size4x = 1; - mem.iMaster = 1; - mem.szMaster = SQLITE_MEMORY_SIZE/8; - mem.mnMaster = mem.szMaster; +#if 0 + if( mem3.mutex==0 ){ + mem3.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); } - sqlite3_mutex_enter(mem.mutex); -} - -/* -** Return the amount of memory currently checked out. -*/ -sqlite3_int64 sqlite3_memory_used(void){ - sqlite3_int64 n; - memsys3Enter(); - n = SQLITE_MEMORY_SIZE - mem.szMaster*8; - sqlite3_mutex_leave(mem.mutex); - return n; + sqlite3_mutex_enter(mem3.mutex); +#endif } - -/* -** Return the maximum amount of memory that has ever been -** checked out since either the beginning of this process -** or since the most recent reset. -*/ -sqlite3_int64 sqlite3_memory_highwater(int resetFlag){ - sqlite3_int64 n; - memsys3Enter(); - n = SQLITE_MEMORY_SIZE - mem.mnMaster*8; - if( resetFlag ){ - mem.mnMaster = mem.szMaster; - } - sqlite3_mutex_leave(mem.mutex); - return n; -} - -/* -** Change the alarm callback. -** -** This is a no-op for the static memory allocator. The purpose -** of the memory alarm is to support sqlite3_soft_heap_limit(). -** But with this memory allocator, the soft_heap_limit is really -** a hard limit that is fixed at SQLITE_MEMORY_SIZE. -*/ -int sqlite3_memory_alarm( - void(*xCallback)(void *pArg, sqlite3_int64 used,int N), - void *pArg, - sqlite3_int64 iThreshold -){ - return SQLITE_OK; +static void memsys3Leave(void){ } /* ** Called when we are unable to satisfy an allocation of nBytes. */ static void memsys3OutOfMemory(int nByte){ - if( !mem.alarmBusy ){ - mem.alarmBusy = 1; - assert( sqlite3_mutex_held(mem.mutex) ); - sqlite3_mutex_leave(mem.mutex); + if( !mem3.alarmBusy ){ + mem3.alarmBusy = 1; + assert( sqlite3_mutex_held(mem3.mutex) ); + sqlite3_mutex_leave(mem3.mutex); sqlite3_release_memory(nByte); - sqlite3_mutex_enter(mem.mutex); - mem.alarmBusy = 0; + sqlite3_mutex_enter(mem3.mutex); + mem3.alarmBusy = 0; } } -/* -** Return the size of an outstanding allocation, in bytes. The -** size returned omits the 8-byte header overhead. This only -** works for chunks that are currently checked out. -*/ -int sqlite3MallocSize(void *p){ - int iSize = 0; - if( p ){ - Mem3Block *pBlock = (Mem3Block*)p; - assert( (pBlock[-1].u.hdr.size4x&1)!=0 ); - iSize = (pBlock[-1].u.hdr.size4x&~3)*2 - 4; - } - return iSize; -} - -/* -** Initialize the memmory allocation subsystem. -*/ -int sqlite3MallocInit(void){ - return SQLITE_OK; -} /* ** Chunk i is a free chunk that has been unlinked. Adjust its @@ -311,62 +251,62 @@ int sqlite3MallocInit(void){ */ static void *memsys3Checkout(u32 i, int nBlock){ u32 x; - assert( sqlite3_mutex_held(mem.mutex) ); + assert( sqlite3_mutex_held(mem3.mutex) ); assert( i>=1 ); - assert( mem.aPool[i-1].u.hdr.size4x/4==nBlock ); - assert( mem.aPool[i+nBlock-1].u.hdr.prevSize==nBlock ); - x = mem.aPool[i-1].u.hdr.size4x; - mem.aPool[i-1].u.hdr.size4x = nBlock*4 | 1 | (x&2); - mem.aPool[i+nBlock-1].u.hdr.prevSize = nBlock; - mem.aPool[i+nBlock-1].u.hdr.size4x |= 2; - return &mem.aPool[i]; + assert( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ); + assert( mem3.aPool[i+nBlock-1].u.hdr.prevSize==nBlock ); + x = mem3.aPool[i-1].u.hdr.size4x; + mem3.aPool[i-1].u.hdr.size4x = nBlock*4 | 1 | (x&2); + mem3.aPool[i+nBlock-1].u.hdr.prevSize = nBlock; + mem3.aPool[i+nBlock-1].u.hdr.size4x |= 2; + return &mem3.aPool[i]; } /* -** Carve a piece off of the end of the mem.iMaster free chunk. +** Carve a piece off of the end of the mem3.iMaster free chunk. ** Return a pointer to the new allocation. Or, if the master chunk ** is not large enough, return 0. */ static void *memsys3FromMaster(int nBlock){ - assert( sqlite3_mutex_held(mem.mutex) ); - assert( mem.szMaster>=nBlock ); - if( nBlock>=mem.szMaster-1 ){ + assert( sqlite3_mutex_held(mem3.mutex) ); + assert( mem3.szMaster>=nBlock ); + if( nBlock>=mem3.szMaster-1 ){ /* Use the entire master */ - void *p = memsys3Checkout(mem.iMaster, mem.szMaster); - mem.iMaster = 0; - mem.szMaster = 0; - mem.mnMaster = 0; + void *p = memsys3Checkout(mem3.iMaster, mem3.szMaster); + mem3.iMaster = 0; + mem3.szMaster = 0; + mem3.mnMaster = 0; return p; }else{ /* Split the master block. Return the tail. */ u32 newi, x; - newi = mem.iMaster + mem.szMaster - nBlock; - assert( newi > mem.iMaster+1 ); - mem.aPool[mem.iMaster+mem.szMaster-1].u.hdr.prevSize = nBlock; - mem.aPool[mem.iMaster+mem.szMaster-1].u.hdr.size4x |= 2; - mem.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1; - mem.szMaster -= nBlock; - mem.aPool[newi-1].u.hdr.prevSize = mem.szMaster; - x = mem.aPool[mem.iMaster-1].u.hdr.size4x & 2; - mem.aPool[mem.iMaster-1].u.hdr.size4x = mem.szMaster*4 | x; - if( mem.szMaster < mem.mnMaster ){ - mem.mnMaster = mem.szMaster; + newi = mem3.iMaster + mem3.szMaster - nBlock; + assert( newi > mem3.iMaster+1 ); + mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = nBlock; + mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x |= 2; + mem3.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1; + mem3.szMaster -= nBlock; + mem3.aPool[newi-1].u.hdr.prevSize = mem3.szMaster; + x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2; + mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x; + if( mem3.szMaster < mem3.mnMaster ){ + mem3.mnMaster = mem3.szMaster; } - return (void*)&mem.aPool[newi]; + return (void*)&mem3.aPool[newi]; } } /* ** *pRoot is the head of a list of free chunks of the same size ** or same size hash. In other words, *pRoot is an entry in either -** mem.aiSmall[] or mem.aiHash[]. +** mem3.aiSmall[] or mem3.aiHash[]. ** ** This routine examines all entries on the given list and tries ** to coalesce each entries with adjacent free chunks. ** -** If it sees a chunk that is larger than mem.iMaster, it replaces -** the current mem.iMaster with the new larger chunk. In order for -** this mem.iMaster replacement to work, the master chunk must be +** If it sees a chunk that is larger than mem3.iMaster, it replaces +** the current mem3.iMaster with the new larger chunk. In order for +** this mem3.iMaster replacement to work, the master chunk must be ** linked into the hash tables. That is not the normal state of ** affairs, of course. The calling routine must link the master ** chunk before invoking this routine, then must unlink the (possibly @@ -375,31 +315,31 @@ static void *memsys3FromMaster(int nBlock){ static void memsys3Merge(u32 *pRoot){ u32 iNext, prev, size, i, x; - assert( sqlite3_mutex_held(mem.mutex) ); + assert( sqlite3_mutex_held(mem3.mutex) ); for(i=*pRoot; i>0; i=iNext){ - iNext = mem.aPool[i].u.list.next; - size = mem.aPool[i-1].u.hdr.size4x; + iNext = mem3.aPool[i].u.list.next; + size = mem3.aPool[i-1].u.hdr.size4x; assert( (size&1)==0 ); if( (size&2)==0 ){ memsys3UnlinkFromList(i, pRoot); - assert( i > mem.aPool[i-1].u.hdr.prevSize ); - prev = i - mem.aPool[i-1].u.hdr.prevSize; + assert( i > mem3.aPool[i-1].u.hdr.prevSize ); + prev = i - mem3.aPool[i-1].u.hdr.prevSize; if( prev==iNext ){ - iNext = mem.aPool[prev].u.list.next; + iNext = mem3.aPool[prev].u.list.next; } memsys3Unlink(prev); size = i + size/4 - prev; - x = mem.aPool[prev-1].u.hdr.size4x & 2; - mem.aPool[prev-1].u.hdr.size4x = size*4 | x; - mem.aPool[prev+size-1].u.hdr.prevSize = size; + x = mem3.aPool[prev-1].u.hdr.size4x & 2; + mem3.aPool[prev-1].u.hdr.size4x = size*4 | x; + mem3.aPool[prev+size-1].u.hdr.prevSize = size; memsys3Link(prev); i = prev; }else{ size /= 4; } - if( size>mem.szMaster ){ - mem.iMaster = i; - mem.szMaster = size; + if( size>mem3.szMaster ){ + mem3.iMaster = i; + mem3.szMaster = size; } } } @@ -413,14 +353,14 @@ static void *memsys3Malloc(int nByte){ int nBlock; int toFree; - assert( sqlite3_mutex_held(mem.mutex) ); + assert( sqlite3_mutex_held(mem3.mutex) ); assert( sizeof(Mem3Block)==8 ); if( nByte<=12 ){ nBlock = 2; }else{ nBlock = (nByte + 11)/8; } - assert( nBlock >= 2 ); + assert( nBlock>=2 ); /* STEP 1: ** Look for an entry of the correct size in either the small @@ -428,16 +368,16 @@ static void *memsys3Malloc(int nByte){ ** successful most of the time (about 9 times out of 10). */ if( nBlock <= MX_SMALL ){ - i = mem.aiSmall[nBlock-2]; + i = mem3.aiSmall[nBlock-2]; if( i>0 ){ - memsys3UnlinkFromList(i, &mem.aiSmall[nBlock-2]); + memsys3UnlinkFromList(i, &mem3.aiSmall[nBlock-2]); return memsys3Checkout(i, nBlock); } }else{ int hash = nBlock % N_HASH; - for(i=mem.aiHash[hash]; i>0; i=mem.aPool[i].u.list.next){ - if( mem.aPool[i-1].u.hdr.size4x/4==nBlock ){ - memsys3UnlinkFromList(i, &mem.aiHash[hash]); + for(i=mem3.aiHash[hash]; i>0; i=mem3.aPool[i].u.list.next){ + if( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ){ + memsys3UnlinkFromList(i, &mem3.aiHash[hash]); return memsys3Checkout(i, nBlock); } } @@ -447,7 +387,7 @@ static void *memsys3Malloc(int nByte){ ** Try to satisfy the allocation by carving a piece off of the end ** of the master chunk. This step usually works if step 1 fails. */ - if( mem.szMaster>=nBlock ){ + if( mem3.szMaster>=nBlock ){ return memsys3FromMaster(nBlock); } @@ -459,22 +399,22 @@ static void *memsys3Malloc(int nByte){ ** of the end of the master chunk. This step happens very ** rarely (we hope!) */ - for(toFree=nBlock*16; toFree<SQLITE_MEMORY_SIZE*2; toFree *= 2){ + for(toFree=nBlock*16; toFree<(mem3.nPool*16); toFree *= 2){ memsys3OutOfMemory(toFree); - if( mem.iMaster ){ - memsys3Link(mem.iMaster); - mem.iMaster = 0; - mem.szMaster = 0; + if( mem3.iMaster ){ + memsys3Link(mem3.iMaster); + mem3.iMaster = 0; + mem3.szMaster = 0; } for(i=0; i<N_HASH; i++){ - memsys3Merge(&mem.aiHash[i]); + memsys3Merge(&mem3.aiHash[i]); } for(i=0; i<MX_SMALL-1; i++){ - memsys3Merge(&mem.aiSmall[i]); + memsys3Merge(&mem3.aiSmall[i]); } - if( mem.szMaster ){ - memsys3Unlink(mem.iMaster); - if( mem.szMaster>=nBlock ){ + if( mem3.szMaster ){ + memsys3Unlink(mem3.iMaster); + if( mem3.szMaster>=nBlock ){ return memsys3FromMaster(nBlock); } } @@ -491,68 +431,76 @@ void memsys3Free(void *pOld){ Mem3Block *p = (Mem3Block*)pOld; int i; u32 size, x; - assert( sqlite3_mutex_held(mem.mutex) ); - assert( p>mem.aPool && p<&mem.aPool[SQLITE_MEMORY_SIZE/8] ); - i = p - mem.aPool; - assert( (mem.aPool[i-1].u.hdr.size4x&1)==1 ); - size = mem.aPool[i-1].u.hdr.size4x/4; - assert( i+size<=SQLITE_MEMORY_SIZE/8+1 ); - mem.aPool[i-1].u.hdr.size4x &= ~1; - mem.aPool[i+size-1].u.hdr.prevSize = size; - mem.aPool[i+size-1].u.hdr.size4x &= ~2; + assert( sqlite3_mutex_held(mem3.mutex) ); + assert( p>mem3.aPool && p<&mem3.aPool[mem3.nPool] ); + i = p - mem3.aPool; + assert( (mem3.aPool[i-1].u.hdr.size4x&1)==1 ); + size = mem3.aPool[i-1].u.hdr.size4x/4; + assert( i+size<=mem3.nPool+1 ); + mem3.aPool[i-1].u.hdr.size4x &= ~1; + mem3.aPool[i+size-1].u.hdr.prevSize = size; + mem3.aPool[i+size-1].u.hdr.size4x &= ~2; memsys3Link(i); /* Try to expand the master using the newly freed chunk */ - if( mem.iMaster ){ - while( (mem.aPool[mem.iMaster-1].u.hdr.size4x&2)==0 ){ - size = mem.aPool[mem.iMaster-1].u.hdr.prevSize; - mem.iMaster -= size; - mem.szMaster += size; - memsys3Unlink(mem.iMaster); - x = mem.aPool[mem.iMaster-1].u.hdr.size4x & 2; - mem.aPool[mem.iMaster-1].u.hdr.size4x = mem.szMaster*4 | x; - mem.aPool[mem.iMaster+mem.szMaster-1].u.hdr.prevSize = mem.szMaster; + if( mem3.iMaster ){ + while( (mem3.aPool[mem3.iMaster-1].u.hdr.size4x&2)==0 ){ + size = mem3.aPool[mem3.iMaster-1].u.hdr.prevSize; + mem3.iMaster -= size; + mem3.szMaster += size; + memsys3Unlink(mem3.iMaster); + x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2; + mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x; + mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster; } - x = mem.aPool[mem.iMaster-1].u.hdr.size4x & 2; - while( (mem.aPool[mem.iMaster+mem.szMaster-1].u.hdr.size4x&1)==0 ){ - memsys3Unlink(mem.iMaster+mem.szMaster); - mem.szMaster += mem.aPool[mem.iMaster+mem.szMaster-1].u.hdr.size4x/4; - mem.aPool[mem.iMaster-1].u.hdr.size4x = mem.szMaster*4 | x; - mem.aPool[mem.iMaster+mem.szMaster-1].u.hdr.prevSize = mem.szMaster; + x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2; + while( (mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x&1)==0 ){ + memsys3Unlink(mem3.iMaster+mem3.szMaster); + mem3.szMaster += mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x/4; + mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x; + mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster; } } } /* -** Allocate nBytes of memory +** Allocate nBytes of memory. */ -void *sqlite3_malloc(int nBytes){ - sqlite3_int64 *p = 0; - if( nBytes>0 ){ - memsys3Enter(); - p = memsys3Malloc(nBytes); - sqlite3_mutex_leave(mem.mutex); - } +static void *mempoolMalloc(int nBytes){ + sqlite3_int64 *p; + assert( nBytes>0 ); /* malloc.c filters out 0 byte requests */ + memsys3Enter(); + p = memsys3Malloc(nBytes); + memsys3Leave(); return (void*)p; } /* ** Free memory. */ -void sqlite3_free(void *pPrior){ - if( pPrior==0 ){ - return; - } - assert( mem.mutex!=0 ); - sqlite3_mutex_enter(mem.mutex); +void mempoolFree(void *pPrior){ + assert( pPrior ); + memsys3Enter(); memsys3Free(pPrior); - sqlite3_mutex_leave(mem.mutex); + memsys3Leave(); +} + +/* +** Return the size of an outstanding allocation, in bytes. The +** size returned omits the 8-byte header overhead. This only +** works for chunks that are currently checked out. +*/ +static int mempoolSize(void *p){ + Mem3Block *pBlock = (Mem3Block*)p; + assert( pBlock ); + assert( (pBlock[-1].u.hdr.size4x&1)!=0 ); + return (pBlock[-1].u.hdr.size4x&~3)*2 - 4; } /* ** Change the size of an existing memory allocation */ -void *sqlite3_realloc(void *pPrior, int nBytes){ +void *mempoolRealloc(void *pPrior, int nBytes){ int nOld; void *p; if( pPrior==0 ){ @@ -562,29 +510,53 @@ void *sqlite3_realloc(void *pPrior, int nBytes){ sqlite3_free(pPrior); return 0; } - assert( mem.mutex!=0 ); - nOld = sqlite3MallocSize(pPrior); + nOld = mempoolSize(pPrior); if( nBytes<=nOld && nBytes>=nOld-128 ){ return pPrior; } - sqlite3_mutex_enter(mem.mutex); - p = memsys3Malloc(nBytes); + memsys3Enter(); + p = mempoolMalloc(nBytes); if( p ){ if( nOld<nBytes ){ memcpy(p, pPrior, nOld); }else{ memcpy(p, pPrior, nBytes); } - memsys3Free(pPrior); + mempoolFree(pPrior); } - sqlite3_mutex_leave(mem.mutex); + memsys3Leave(); return p; } /* +** Round up a request size to the next valid allocation size. +*/ +static int mempoolRoundup(int n){ + /* TODO: Fix me */ + return n; +} + +/* +** Initialize this module. +*/ +static int mempoolInit(void *NotUsed){ + return SQLITE_OK; +} + +/* +** Deinitialize this module. +*/ +static void mempoolShutdown(void *NotUsed){ + return; +} + + + +/* ** Open the file indicated and write a log of all unfreed memory ** allocations into that log. */ +#if 0 void sqlite3MemdebugDump(const char *zFilename){ #ifdef SQLITE_DEBUG FILE *out; @@ -603,51 +575,51 @@ void sqlite3MemdebugDump(const char *zFilename){ memsys3Enter(); fprintf(out, "CHUNKS:\n"); for(i=1; i<=SQLITE_MEMORY_SIZE/8; i+=size/4){ - size = mem.aPool[i-1].u.hdr.size4x; + size = mem3.aPool[i-1].u.hdr.size4x; if( size/4<=1 ){ - fprintf(out, "%p size error\n", &mem.aPool[i]); + fprintf(out, "%p size error\n", &mem3.aPool[i]); assert( 0 ); break; } - if( (size&1)==0 && mem.aPool[i+size/4-1].u.hdr.prevSize!=size/4 ){ - fprintf(out, "%p tail size does not match\n", &mem.aPool[i]); + if( (size&1)==0 && mem3.aPool[i+size/4-1].u.hdr.prevSize!=size/4 ){ + fprintf(out, "%p tail size does not match\n", &mem3.aPool[i]); assert( 0 ); break; } - if( ((mem.aPool[i+size/4-1].u.hdr.size4x&2)>>1)!=(size&1) ){ - fprintf(out, "%p tail checkout bit is incorrect\n", &mem.aPool[i]); + if( ((mem3.aPool[i+size/4-1].u.hdr.size4x&2)>>1)!=(size&1) ){ + fprintf(out, "%p tail checkout bit is incorrect\n", &mem3.aPool[i]); assert( 0 ); break; } if( size&1 ){ - fprintf(out, "%p %6d bytes checked out\n", &mem.aPool[i], (size/4)*8-8); + fprintf(out, "%p %6d bytes checked out\n", &mem3.aPool[i], (size/4)*8-8); }else{ - fprintf(out, "%p %6d bytes free%s\n", &mem.aPool[i], (size/4)*8-8, - i==mem.iMaster ? " **master**" : ""); + fprintf(out, "%p %6d bytes free%s\n", &mem3.aPool[i], (size/4)*8-8, + i==mem3.iMaster ? " **master**" : ""); } } for(i=0; i<MX_SMALL-1; i++){ - if( mem.aiSmall[i]==0 ) continue; + if( mem3.aiSmall[i]==0 ) continue; fprintf(out, "small(%2d):", i); - for(j = mem.aiSmall[i]; j>0; j=mem.aPool[j].u.list.next){ - fprintf(out, " %p(%d)", &mem.aPool[j], - (mem.aPool[j-1].u.hdr.size4x/4)*8-8); + for(j = mem3.aiSmall[i]; j>0; j=mem3.aPool[j].u.list.next){ + fprintf(out, " %p(%d)", &mem3.aPool[j], + (mem3.aPool[j-1].u.hdr.size4x/4)*8-8); } fprintf(out, "\n"); } for(i=0; i<N_HASH; i++){ - if( mem.aiHash[i]==0 ) continue; + if( mem3.aiHash[i]==0 ) continue; fprintf(out, "hash(%2d):", i); - for(j = mem.aiHash[i]; j>0; j=mem.aPool[j].u.list.next){ - fprintf(out, " %p(%d)", &mem.aPool[j], - (mem.aPool[j-1].u.hdr.size4x/4)*8-8); + for(j = mem3.aiHash[i]; j>0; j=mem3.aPool[j].u.list.next){ + fprintf(out, " %p(%d)", &mem3.aPool[j], + (mem3.aPool[j-1].u.hdr.size4x/4)*8-8); } fprintf(out, "\n"); } - fprintf(out, "master=%d\n", mem.iMaster); - fprintf(out, "nowUsed=%d\n", SQLITE_MEMORY_SIZE - mem.szMaster*8); - fprintf(out, "mxUsed=%d\n", SQLITE_MEMORY_SIZE - mem.mnMaster*8); - sqlite3_mutex_leave(mem.mutex); + fprintf(out, "master=%d\n", mem3.iMaster); + fprintf(out, "nowUsed=%d\n", SQLITE_MEMORY_SIZE - mem3.szMaster*8); + fprintf(out, "mxUsed=%d\n", SQLITE_MEMORY_SIZE - mem3.mnMaster*8); + sqlite3_mutex_leave(mem3.mutex); if( out==stdout ){ fflush(stdout); }else{ @@ -655,6 +627,46 @@ void sqlite3MemdebugDump(const char *zFilename){ } #endif } +#endif +/* +** This routine is the only routine in this file with external +** linkage. +** +** Populate the low-level memory allocation function pointers in +** sqlite3Config.m with pointers to the routines in this file. The +** arguments specify the block of memory to manage. +** +** This routine is only called by sqlite3_config(), and therefore +** is not required to be threadsafe (it is not). +*/ +void sqlite3MemSetMempool(u8 *pBlock, int nBlock){ + static const sqlite3_mem_methods mempoolMethods = { + mempoolMalloc, + mempoolFree, + mempoolRealloc, + mempoolSize, + mempoolRoundup, + mempoolInit, + mempoolShutdown, + 0 + }; + + /* Configure the functions to call to allocate memory. */ + sqlite3_config(SQLITE_CONFIG_MALLOC, &mempoolMethods); + + /* Store a pointer to the memory block in global structure mem3. */ + assert( sizeof(Mem3Block)==8 ); + mem3.aPool = (Mem3Block *)pBlock; + mem3.nPool = (nBlock / sizeof(Mem3Block)) - 2; + + /* Initialize the master block. */ + mem3.szMaster = mem3.nPool; + mem3.mnMaster = mem3.szMaster; + mem3.iMaster = 1; + mem3.aPool[0].u.hdr.size4x = (mem3.szMaster<<2) + 2; + mem3.aPool[mem3.nPool].u.hdr.prevSize = mem3.nPool; + mem3.aPool[mem3.nPool].u.hdr.size4x = 1; +} -#endif /* !SQLITE_MEMORY_SIZE */ +#endif /* SQLITE_MEMPOOL_MALLOC */ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 3289386aa..316dc1674 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -30,7 +30,7 @@ ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. ** -** @(#) $Id: sqlite.h.in,v 1.354 2008/06/24 09:52:39 danielk1977 Exp $ +** @(#) $Id: sqlite.h.in,v 1.355 2008/06/24 19:02:55 danielk1977 Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -1115,6 +1115,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ +#define SQLITE_CONFIG_MEMPOOL 12 /* u8*, int */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes {F12200} diff --git a/src/test_malloc.c b/src/test_malloc.c index 582abeed3..b0762d7ab 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -13,7 +13,7 @@ ** This file contains code used to implement test interfaces to the ** memory allocation subsystem. ** -** $Id: test_malloc.c,v 1.28 2008/06/20 14:59:51 danielk1977 Exp $ +** $Id: test_malloc.c,v 1.29 2008/06/24 19:02:55 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -931,6 +931,38 @@ static int test_config_pagecache( } /* +** Usage: sqlite3_config_mempool NBYTE +** +*/ +static int test_config_mempool( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int sz, rc; + Tcl_Obj *pResult; + static char buf[1000000]; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "NBYTE"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR; + if( sz<=0 ){ + sqlite3_mem_methods m; + memset(&m, 0, sizeof(sqlite3_mem_methods)); + rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &m); + }else{ + if( sz>sizeof(buf) ){ + sz = sizeof(buf); + } + rc = sqlite3_config(SQLITE_CONFIG_MEMPOOL, buf, sz); + } + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + return TCL_OK; +} + +/* ** Usage: sqlite3_status OPCODE RESETFLAG ** ** Return a list of three elements which are the sqlite3_status() return @@ -1029,6 +1061,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ { "sqlite3_memdebug_log", test_memdebug_log }, { "sqlite3_config_scratch", test_config_scratch }, { "sqlite3_config_pagecache", test_config_pagecache }, + { "sqlite3_config_mempool", test_config_mempool }, { "sqlite3_status", test_status }, { "install_malloc_faultsim", test_install_malloc_faultsim }, }; |