aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/malloc.c82
-rw-r--r--src/sqlite.h.in12
-rw-r--r--src/test_malloc.c85
3 files changed, 163 insertions, 16 deletions
diff --git a/src/malloc.c b/src/malloc.c
index cdfd6a9ea..a278e4816 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -12,7 +12,7 @@
**
** Memory allocation functions used throughout sqlite.
**
-** $Id: malloc.c,v 1.19 2008/06/18 17:09:10 danielk1977 Exp $
+** $Id: malloc.c,v 1.20 2008/06/18 18:12:04 drh Exp $
*/
#include "sqliteInt.h"
#include <stdarg.h>
@@ -43,6 +43,7 @@ void sqlite3_soft_heap_limit(int n){
}else{
iLimit = n;
}
+ sqlite3_initialize();
if( iLimit>0 ){
sqlite3_memory_alarm(softHeapLimitEnforcer, 0, iLimit);
}else{
@@ -86,6 +87,18 @@ static struct {
int alarmBusy;
/*
+ ** Pointers to the end of sqlite3Config.pScratch and
+ ** sqlite3Config.pPage to a block of memory that records
+ ** which pages are available.
+ */
+ u32 *aScratchFree;
+ u32 *aPageFree;
+
+ /* Number of free pages for scratch and page-cache memory */
+ u32 nScratchFree;
+ u32 nPageFree;
+
+ /*
** Performance statistics
*/
sqlite3_int64 nowUsed; /* Main memory currently in use */
@@ -102,9 +115,29 @@ int sqlite3MallocInit(void){
sqlite3MemSetDefault();
}
memset(&mem0, 0, sizeof(mem0));
- if( sqlite3Config.bMemstat ){
+ if( sqlite3Config.bCoreMutex ){
mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
}
+ if( sqlite3Config.pScratch && sqlite3Config.szScratch>=3000
+ && sqlite3Config.nScratch>0 ){
+ int i;
+ mem0.aScratchFree = (u32*)&((char*)sqlite3Config.pScratch)
+ [sqlite3Config.szScratch*sqlite3Config.nScratch];
+ for(i=0; i<sqlite3Config.nScratch; i++){ mem0.aScratchFree[i] = i; }
+ mem0.nScratchFree = sqlite3Config.nScratch;
+ }else{
+ sqlite3Config.pScratch = 0;
+ }
+ if( sqlite3Config.pPage && sqlite3Config.szPage>=512
+ && sqlite3Config.nPage>0 ){
+ int i;
+ mem0.aPageFree = (u32*)&((char*)sqlite3Config.pPage)
+ [sqlite3Config.szPage*sqlite3Config.nPage];
+ for(i=0; i<sqlite3Config.nPage; i++){ mem0.aPageFree[i] = i; }
+ mem0.nPageFree = sqlite3Config.nPage;
+ }else{
+ sqlite3Config.pPage = 0;
+ }
return sqlite3Config.m.xInit(sqlite3Config.m.pAppData);
}
@@ -112,7 +145,8 @@ int sqlite3MallocInit(void){
** Deinitialize the memory allocation subsystem.
*/
void sqlite3MallocEnd(void){
- sqlite3Config.m.xShutdown(sqlite3Config.m.pAppData);
+ sqlite3Config.m.xShutdown(sqlite3Config.m.pAppData);
+ memset(&mem0, 0, sizeof(mem0));
}
/*
@@ -120,6 +154,7 @@ void sqlite3MallocEnd(void){
*/
sqlite3_int64 sqlite3_memory_used(void){
sqlite3_int64 n;
+ sqlite3_initialize();
sqlite3_mutex_enter(mem0.mutex);
n = mem0.nowUsed;
sqlite3_mutex_leave(mem0.mutex);
@@ -133,6 +168,7 @@ sqlite3_int64 sqlite3_memory_used(void){
*/
sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
sqlite3_int64 n;
+ sqlite3_initialize();
sqlite3_mutex_enter(mem0.mutex);
n = mem0.mxUsed;
if( resetFlag ){
@@ -252,27 +288,55 @@ void *sqlite3ScratchMalloc(int n){
if( sqlite3FaultStep(SQLITE_FAULTINJECTOR_MALLOC) ){
return 0;
}
+
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
+ /* Verify that no more than one scratch allocation per thread
+ ** is outstanding at one time. (This is only checked in the
+ ** single-threaded case since checking in the multi-threaded case
+ ** would be much more complicated.) */
assert( scratchAllocOut==0 );
scratchAllocOut = 1;
#endif
- if( sqlite3Config.bMemstat ){
- sqlite3_mutex_enter(mem0.mutex);
- if( n>mem0.mxScratchReq ) mem0.mxScratchReq = n;
+
+ sqlite3_mutex_enter(mem0.mutex);
+ if( n>mem0.mxScratchReq ) mem0.mxScratchReq = n;
+ if( mem0.nScratchFree==0 || sqlite3Config.szScratch>=n ){
p = sqlite3Config.m.xMalloc(n);
- sqlite3_mutex_leave(mem0.mutex);
}else{
- p = sqlite3Config.m.xMalloc(n);
+ int i;
+ i = mem0.aScratchFree[--mem0.nScratchFree];
+ i *= sqlite3Config.szScratch;
+ p = (void*)&((char*)sqlite3Config.pScratch)[i];
}
+ sqlite3_mutex_leave(mem0.mutex);
return p;
}
void sqlite3ScratchFree(void *p){
if( p ){
+
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
+ /* Verify that no more than one scratch allocation per thread
+ ** is outstanding at one time. (This is only checked in the
+ ** single-threaded case since checking in the multi-threaded case
+ ** would be much more complicated.) */
assert( scratchAllocOut==1 );
scratchAllocOut = 0;
#endif
- sqlite3Config.m.xFree(p);
+
+ if( sqlite3Config.pScratch==0
+ || p<sqlite3Config.pScratch
+ || p>=(void*)mem0.aScratchFree ){
+ sqlite3Config.m.xFree(p);
+ }else{
+ int i;
+ sqlite3_mutex_enter(mem0.mutex);
+ assert( mem0.nScratchFree<sqlite3Config.nScratch );
+ i = p - sqlite3Config.pScratch;
+ i /= sqlite3Config.szScratch;
+ assert( i>=0 && i<sqlite3Config.nScratch );
+ mem0.aScratchFree[mem0.nScratchFree++] = i;
+ sqlite3_mutex_leave(mem0.mutex);
+ }
}
}
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 737904db9..b14d32bfb 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.334 2008/06/18 13:47:04 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.335 2008/06/18 18:12:04 drh Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
@@ -1055,8 +1055,9 @@ struct sqlite3_mem_methods {
** <dt>SQLITE_CONFIG_SCRATCH</dt>
** <dd>This option specifies a static memory buffer that SQLite can use for
** scratch memory. There are three arguments: A pointer to the memory, the
-** size of each scratch buffer (sz), and the number of buffers (N). The first
-** argument should point to an allocation of at least sz*N bytes of memory.
+** size of each scratch buffer (sz), and the number of buffers (N). The sz
+** argument must be a multiple of 16. The first
+** argument should point to an allocation of at least (sz+1)*N bytes of memory.
** SQLite will use no more than one scratch buffer at once per thread, so
** N should be set to the expected maximum number of threads. The sz
** parameter should be 6 times the size of the largest database page size.
@@ -1070,8 +1071,9 @@ struct sqlite3_mem_methods {
** <dd>This option specifies a static memory buffer that SQLite can use for
** the database page cache. There are three arguments:
** A pointer to the memory, the
-** size of each page buffer (sz), and the number of pages (N). The first
-** argument should point to an allocation of at least sz*N bytes of memory.
+** size of each page buffer (sz), and the number of pages (N). The sz
+** argument must be a power of two between 512 and 32768. The first
+** argument should point to an allocation of at least (sz+4)*N bytes of memory.
** SQLite will use the memory provided by the first argument to satisfy
** its memory needs for the first N pages that it adds to cache. If
** additional page cache memory is needed beyond what is provided by
diff --git a/src/test_malloc.c b/src/test_malloc.c
index da2a7b5f9..95ee36561 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.23 2008/05/29 02:57:48 shane Exp $
+** $Id: test_malloc.c,v 1.24 2008/06/18 18:12:04 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
@@ -652,6 +652,85 @@ static int test_memdebug_log(
}
/*
+** Usage: sqlite3_config_scratch SIZE N
+**
+** Set the scratch memory buffer using SQLITE_CONFIG_SCRATCH.
+** The buffer is static and is of limited size. N might be
+** adjusted downward as needed to accomodate the requested size.
+** The revised value of N is returned.
+**
+** A negative SIZE causes the buffer pointer to be NULL.
+*/
+static int test_config_scratch(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int sz, N, rc;
+ Tcl_Obj *pResult;
+ static char buf[20000];
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[3], &N) ) return TCL_ERROR;
+ if( sz<0 ){
+ rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, 0, 0, 0);
+ }else if( sz==0 ){
+ int mx = sizeof(buf)/(sz+4);
+ if( N>mx ) N = mx;
+ rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, buf, sz, N);
+ }
+ pResult = Tcl_NewObj();
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
+ Tcl_SetObjResult(interp, pResult);
+ return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_config_pagecache SIZE N
+**
+** Set the page-cache memory buffer using SQLITE_CONFIG_PAGECACHE.
+** The buffer is static and is of limited size. N might be
+** adjusted downward as needed to accomodate the requested size.
+** The revised value of N is returned.
+**
+** A negative SIZE causes the buffer pointer to be NULL.
+*/
+static int test_config_pagecache(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int sz, N, rc;
+ Tcl_Obj *pResult;
+ static char buf[100000];
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
+ if( Tcl_GetIntFromObj(interp, objv[3], &N) ) return TCL_ERROR;
+ if( sz<0 ){
+ rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, 0, 0, 0);
+ }else if( sz==0 ){
+ int mx = sizeof(buf)/(sz+4);
+ if( N>mx ) N = mx;
+ rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, buf, sz, N);
+ }
+ pResult = Tcl_NewObj();
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
+ Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
+ Tcl_SetObjResult(interp, pResult);
+ return TCL_OK;
+}
+
+
+/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest_malloc_Init(Tcl_Interp *interp){
@@ -672,7 +751,9 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){
{ "sqlite3_memdebug_pending", test_memdebug_pending },
{ "sqlite3_memdebug_settitle", test_memdebug_settitle },
{ "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count },
- { "sqlite3_memdebug_log", test_memdebug_log },
+ { "sqlite3_memdebug_log", test_memdebug_log },
+ { "sqlite3_config_scratch", test_config_scratch },
+ { "sqlite3_config_pagecache", test_config_pagecache },
};
int i;
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){