aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordanielk1977 <danielk1977@noemail.net>2008-03-26 18:34:43 +0000
committerdanielk1977 <danielk1977@noemail.net>2008-03-26 18:34:43 +0000
commitdfb316d43242a61212feec6c1293b4519f348a79 (patch)
treeeef6a718c5ecdc19aa03a165b8bdbc4c4ed96297 /src
parent7e2e6dc9505b9dec7fd809020ede9db4bfd9a694 (diff)
downloadsqlite-dfb316d43242a61212feec6c1293b4519f348a79.tar.gz
sqlite-dfb316d43242a61212feec6c1293b4519f348a79.zip
Changes to delay freeing buffers associated with vdbe memory cells until either sqlite3_finalize() or sqlite3_release_memory() is called. (CVS 4922)
FossilOrigin-Name: 8c2f69521f13bc24cf005520efbe0589eeadd763
Diffstat (limited to 'src')
-rw-r--r--src/malloc.c6
-rw-r--r--src/mutex.c4
-rw-r--r--src/mutex_os2.c3
-rw-r--r--src/mutex_unix.c3
-rw-r--r--src/mutex_w32.c4
-rw-r--r--src/sqlite.h.in4
-rw-r--r--src/vdbe.h5
-rw-r--r--src/vdbeInt.h5
-rw-r--r--src/vdbeapi.c161
-rw-r--r--src/vdbeaux.c50
10 files changed, 217 insertions, 28 deletions
diff --git a/src/malloc.c b/src/malloc.c
index a04734868..55d5c76de 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -12,7 +12,7 @@
** Memory allocation functions used throughout sqlite.
**
**
-** $Id: malloc.c,v 1.14 2007/10/20 16:36:31 drh Exp $
+** $Id: malloc.c,v 1.15 2008/03/26 18:34:43 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <stdarg.h>
@@ -59,7 +59,9 @@ void sqlite3_soft_heap_limit(int n){
*/
int sqlite3_release_memory(int n){
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- return sqlite3PagerReleaseMemory(n);
+ int nRet = sqlite3VdbeReleaseMemory(n);
+ nRet += sqlite3PagerReleaseMemory(n-nRet);
+ return nRet;
#else
return SQLITE_OK;
#endif
diff --git a/src/mutex.c b/src/mutex.c
index 5815ff216..f4372c961 100644
--- a/src/mutex.c
+++ b/src/mutex.c
@@ -19,7 +19,7 @@
** implementation is suitable for testing.
** debugging purposes
**
-** $Id: mutex.c,v 1.16 2007/09/10 16:13:00 danielk1977 Exp $
+** $Id: mutex.c,v 1.17 2008/03/26 18:34:43 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -44,7 +44,7 @@ struct sqlite3_mutex {
** that means that a mutex could not be allocated.
*/
sqlite3_mutex *sqlite3_mutex_alloc(int id){
- static sqlite3_mutex aStatic[5];
+ static sqlite3_mutex aStatic[6];
sqlite3_mutex *pNew = 0;
switch( id ){
case SQLITE_MUTEX_FAST:
diff --git a/src/mutex_os2.c b/src/mutex_os2.c
index cbffeeebc..ed5477a54 100644
--- a/src/mutex_os2.c
+++ b/src/mutex_os2.c
@@ -11,7 +11,7 @@
*************************************************************************
** This file contains the C functions that implement mutexes for OS/2
**
-** $Id: mutex_os2.c,v 1.5 2008/02/01 19:42:38 pweilbacher Exp $
+** $Id: mutex_os2.c,v 1.6 2008/03/26 18:34:43 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -101,6 +101,7 @@ sqlite3_mutex *sqlite3_mutex_alloc(int iType){
{ OS2_MUTEX_INITIALIZER, },
{ OS2_MUTEX_INITIALIZER, },
{ OS2_MUTEX_INITIALIZER, },
+ { OS2_MUTEX_INITIALIZER, },
};
if ( !isInit ){
APIRET rc;
diff --git a/src/mutex_unix.c b/src/mutex_unix.c
index 93e7e9a19..a21798af6 100644
--- a/src/mutex_unix.c
+++ b/src/mutex_unix.c
@@ -11,7 +11,7 @@
*************************************************************************
** This file contains the C functions that implement mutexes for pthreads
**
-** $Id: mutex_unix.c,v 1.5 2007/11/28 14:04:57 drh Exp $
+** $Id: mutex_unix.c,v 1.6 2008/03/26 18:34:43 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -88,6 +88,7 @@ sqlite3_mutex *sqlite3_mutex_alloc(int iType){
{ PTHREAD_MUTEX_INITIALIZER, },
{ PTHREAD_MUTEX_INITIALIZER, },
{ PTHREAD_MUTEX_INITIALIZER, },
+ { PTHREAD_MUTEX_INITIALIZER, },
};
sqlite3_mutex *p;
switch( iType ){
diff --git a/src/mutex_w32.c b/src/mutex_w32.c
index 1944ccc79..7fc73da12 100644
--- a/src/mutex_w32.c
+++ b/src/mutex_w32.c
@@ -11,7 +11,7 @@
*************************************************************************
** This file contains the C functions that implement mutexes for win32
**
-** $Id: mutex_w32.c,v 1.5 2007/10/05 15:08:01 drh Exp $
+** $Id: mutex_w32.c,v 1.6 2008/03/26 18:34:43 danielk1977 Exp $
*/
#include "sqliteInt.h"
@@ -111,7 +111,7 @@ sqlite3_mutex *sqlite3_mutex_alloc(int iType){
break;
}
default: {
- static sqlite3_mutex staticMutexes[5];
+ static sqlite3_mutex staticMutexes[6];
static int isInit = 0;
while( !isInit ){
static long lock = 0;
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 335772c45..d6c68ccb2 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.300 2008/03/24 12:51:46 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.301 2008/03/26 18:34:43 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
@@ -5440,6 +5440,7 @@ int sqlite3_vfs_unregister(sqlite3_vfs*);
** <li> SQLITE_MUTEX_STATIC_MEM2
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
+** <li> SQLITE_MUTEX_STATIC_LRU2
** </ul> {END}
**
** {F17015} The first two constants cause sqlite3_mutex_alloc() to create
@@ -5552,6 +5553,7 @@ int sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_STATIC_MEM2 4 /* sqlite3_release_memory() */
#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */
#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */
+#define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */
/*
** CAPI3REF: Low-Level Control Of Database Files {F11300}
diff --git a/src/vdbe.h b/src/vdbe.h
index ad6d87004..5d6bc905a 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -15,7 +15,7 @@
** or VDBE. The VDBE implements an abstract machine that runs a
** simple program to access and modify the underlying database.
**
-** $Id: vdbe.h,v 1.128 2008/03/25 17:23:34 drh Exp $
+** $Id: vdbe.h,v 1.129 2008/03/26 18:34:43 danielk1977 Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
@@ -174,7 +174,7 @@ int sqlite3VdbeCurrentAddr(Vdbe*);
void sqlite3VdbeTrace(Vdbe*,FILE*);
#endif
void sqlite3VdbeResetStepResult(Vdbe*);
-int sqlite3VdbeReset(Vdbe*);
+int sqlite3VdbeReset(Vdbe*, int);
void sqlite3VdbeSetNumCols(Vdbe*,int);
int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, int);
void sqlite3VdbeCountChanges(Vdbe*);
@@ -182,6 +182,7 @@ sqlite3 *sqlite3VdbeDb(Vdbe*);
void sqlite3VdbeSetSql(Vdbe*, const char *z, int n);
void sqlite3VdbeSwap(Vdbe*,Vdbe*);
+int sqlite3VdbeReleaseMemory(int);
UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,void*,int);
void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*);
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 08821f266..d492a26b4 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -333,6 +333,10 @@ struct Vdbe {
int fetchId; /* Statement number used by sqlite3_fetch_statement */
int lru; /* Counter used for LRU cache replacement */
#endif
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ Vdbe *pLruPrev;
+ Vdbe *pLruNext;
+#endif
};
/*
@@ -414,6 +418,7 @@ int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
const char *sqlite3OpcodeName(int);
int sqlite3VdbeOpcodeHasProperty(int, int);
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
+int sqlite3VdbeReleaseBuffers(Vdbe *p);
#ifndef NDEBUG
void sqlite3VdbeMemSanity(Mem*);
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index dfefae116..c0bde8692 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -16,6 +16,160 @@
#include "sqliteInt.h"
#include "vdbeInt.h"
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+/*
+** The following structure contains pointers to the end points of a
+** doubly-linked list of all compiled SQL statements that may be holding
+** buffers eligible for release when the sqlite3_release_memory() interface is
+** invoked. Access to this list is protected by the SQLITE_MUTEX_STATIC_LRU2
+** mutex.
+**
+** Statements are added to the end of this list when sqlite3_reset() is
+** called. They are removed either when sqlite3_step() or sqlite3_finalize()
+** is called. When statements are added to this list, the associated
+** register array (p->aMem[1..p->nMem]) may contain dynamic buffers that
+** can be freed using sqlite3VdbeReleaseMemory().
+**
+** When statements are added or removed from this list, the mutex
+** associated with the Vdbe being added or removed (Vdbe.db->mutex) is
+** already held. The LRU2 mutex is then obtained, blocking if necessary,
+** the linked-list pointers manipulated and the LRU2 mutex relinquished.
+*/
+struct StatementLruList {
+ Vdbe *pFirst;
+ Vdbe *pLast;
+};
+static struct StatementLruList sqlite3LruStatements;
+
+/*
+** Check that the list looks to be internally consistent. This is used
+** as part of an assert() statement as follows:
+**
+** assert( stmtLruCheck() );
+*/
+static int stmtLruCheck(){
+ Vdbe *p;
+ for(p=sqlite3LruStatements.pFirst; p; p=p->pLruNext){
+ assert(p->pLruNext || p==sqlite3LruStatements.pLast);
+ assert(!p->pLruNext || p->pLruNext->pLruPrev==p);
+ assert(p->pLruPrev || p==sqlite3LruStatements.pFirst);
+ assert(!p->pLruPrev || p->pLruPrev->pLruNext==p);
+ }
+ return 1;
+}
+
+/*
+** Add vdbe p to the end of the statement lru list. It is assumed that
+** p is not already part of the list when this is called. The lru list
+** is protected by the SQLITE_MUTEX_STATIC_LRU mutex.
+*/
+static void stmtLruAdd(Vdbe *p){
+ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU2));
+
+ if( p->pLruPrev || p->pLruNext || sqlite3LruStatements.pFirst==p ){
+ sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU2));
+ return;
+ }
+
+ assert( stmtLruCheck() );
+
+ if( !sqlite3LruStatements.pFirst ){
+ assert( !sqlite3LruStatements.pLast );
+ sqlite3LruStatements.pFirst = p;
+ sqlite3LruStatements.pLast = p;
+ }else{
+ assert( !sqlite3LruStatements.pLast->pLruNext );
+ p->pLruPrev = sqlite3LruStatements.pLast;
+ sqlite3LruStatements.pLast->pLruNext = p;
+ sqlite3LruStatements.pLast = p;
+ }
+
+ assert( stmtLruCheck() );
+
+ sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU2));
+}
+
+/*
+** Assuming the SQLITE_MUTEX_STATIC_LRU2 mutext is already held, remove
+** statement p from the least-recently-used statement list. If the
+** statement is not currently part of the list, this call is a no-op.
+*/
+static void stmtLruRemoveNomutex(Vdbe *p){
+ if( p->pLruPrev || p->pLruNext || p==sqlite3LruStatements.pFirst ){
+ assert( stmtLruCheck() );
+ if( p->pLruNext ){
+ p->pLruNext->pLruPrev = p->pLruPrev;
+ }else{
+ sqlite3LruStatements.pLast = p->pLruPrev;
+ }
+ if( p->pLruPrev ){
+ p->pLruPrev->pLruNext = p->pLruNext;
+ }else{
+ sqlite3LruStatements.pFirst = p->pLruNext;
+ }
+ p->pLruNext = 0;
+ p->pLruPrev = 0;
+ assert( stmtLruCheck() );
+ }
+}
+
+/*
+** Assuming the SQLITE_MUTEX_STATIC_LRU2 mutext is not held, remove
+** statement p from the least-recently-used statement list. If the
+** statement is not currently part of the list, this call is a no-op.
+*/
+static void stmtLruRemove(Vdbe *p){
+ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU2));
+ stmtLruRemoveNomutex(p);
+ sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU2));
+}
+
+/*
+** Try to release n bytes of memory by freeing buffers associated
+** with the memory registers of currently unused vdbes.
+*/
+int sqlite3VdbeReleaseMemory(int n){
+ Vdbe *p;
+ Vdbe *pNext;
+ int nFree = 0;
+
+ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU2));
+ for(p=sqlite3LruStatements.pFirst; p && nFree<n; p=pNext){
+ pNext = p->pLruNext;
+
+ /* For each statement handle in the lru list, attempt to obtain the
+ ** associated database mutex. If it cannot be obtained, continue
+ ** to the next statement handle. It is not possible to block on
+ ** the database mutex - that could cause deadlock.
+ */
+ if( SQLITE_OK==sqlite3_mutex_try(p->db->mutex) ){
+ nFree += sqlite3VdbeReleaseBuffers(p);
+ stmtLruRemoveNomutex(p);
+ sqlite3_mutex_leave(p->db->mutex);
+ }
+ }
+ sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU2));
+
+ return nFree;
+}
+
+/*
+** Call sqlite3Reprepare() on the statement. Remove it from the
+** lru list before doing so, as Reprepare() will free all the
+** memory register buffers anyway.
+*/
+int vdbeReprepare(Vdbe *p){
+ stmtLruRemove(p);
+ return sqlite3Reprepare(p);
+}
+
+#else /* !SQLITE_ENABLE_MEMORY_MANAGEMENT */
+ #define stmtLruRemove(x)
+ #define stmtLruAdd(x)
+ #define vdbeReprepare(x) sqlite3Reprepare(x)
+#endif
+
+
/*
** Return TRUE (non-zero) of the statement supplied as an argument needs
** to be recompiled. A statement needs to be recompiled whenever the
@@ -48,6 +202,7 @@ int sqlite3_finalize(sqlite3_stmt *pStmt){
sqlite3_mutex *mutex = v->db->mutex;
#endif
sqlite3_mutex_enter(mutex);
+ stmtLruRemove(v);
rc = sqlite3VdbeFinalize(v);
sqlite3_mutex_leave(mutex);
}
@@ -69,7 +224,8 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
}else{
Vdbe *v = (Vdbe*)pStmt;
sqlite3_mutex_enter(v->db->mutex);
- rc = sqlite3VdbeReset(v);
+ rc = sqlite3VdbeReset(v, 0);
+ stmtLruAdd(v);
sqlite3VdbeMakeReady(v, -1, 0, 0, 0);
assert( (rc & (v->db->errMask))==rc );
sqlite3_mutex_leave(v->db->mutex);
@@ -306,6 +462,7 @@ static int sqlite3Step(Vdbe *p){
db->activeVdbeCnt++;
p->pc = 0;
+ stmtLruRemove(p);
}
#ifndef SQLITE_OMIT_EXPLAIN
if( p->explain ){
@@ -377,7 +534,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){
sqlite3_mutex_enter(db->mutex);
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
&& cnt++ < 5
- && sqlite3Reprepare(v) ){
+ && vdbeReprepare(v) ){
sqlite3_reset(pStmt);
v->expired = 0;
}
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 9399315cf..6b737d2c8 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -737,19 +737,39 @@ void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){
/*
** Release an array of N Mem elements
*/
-static void releaseMemArray(Mem *p, int N){
+static void releaseMemArray(Mem *p, int N, int freebuffers){
if( p && N ){
sqlite3 *db = p->db;
int malloc_failed = db->mallocFailed;
while( N-->0 ){
assert( N<2 || p[0].db==p[1].db );
- sqlite3VdbeMemRelease(p);
- p++->flags = MEM_Null;
+ if( freebuffers || p->xDel ){
+ sqlite3VdbeMemRelease(p);
+ p->flags = MEM_Null;
+ }
+ p++;
}
db->mallocFailed = malloc_failed;
}
}
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+int sqlite3VdbeReleaseBuffers(Vdbe *p){
+ int ii;
+ int nFree = 0;
+ assert( sqlite3_mutex_held(p->db->mutex) );
+ for(ii=1; ii<=p->nMem; ii++){
+ Mem *pMem = &p->aMem[ii];
+ if( pMem->z && pMem->flags&MEM_Dyn ){
+ assert( !pMem->xDel );
+ nFree += sqlite3MallocSize(pMem->z);
+ sqlite3VdbeMemRelease(pMem);
+ }
+ }
+ return nFree;
+}
+#endif
+
#ifndef SQLITE_OMIT_EXPLAIN
/*
** Give a listing of the program in the virtual machine.
@@ -780,7 +800,7 @@ int sqlite3VdbeList(
** the result, result columns may become dynamic if the user calls
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
*/
- releaseMemArray(pMem, p->nMem);
+ releaseMemArray(pMem, p->nMem, 1);
do{
i = p->pc++;
@@ -1010,7 +1030,7 @@ void sqlite3VdbeMakeReady(
#ifdef SQLITE_DEBUG
for(n=1; n<p->nMem; n++){
assert( p->aMem[n].db==db );
- assert( p->aMem[n].flags==MEM_Null );
+ //assert( p->aMem[n].flags==MEM_Null );
}
#endif
@@ -1090,14 +1110,13 @@ static void closeAllCursorsExceptActiveVtabs(Vdbe *p){
** sorters that were left open. It also deletes the values of
** variables in the aVar[] array.
*/
-static void Cleanup(Vdbe *p){
+static void Cleanup(Vdbe *p, int freebuffers){
int i;
closeAllCursorsExceptActiveVtabs(p);
for(i=1; i<=p->nMem; i++){
MemSetTypeFlag(&p->aMem[i], MEM_Null);
}
-
- releaseMemArray(&p->aMem[1], p->nMem);
+ releaseMemArray(&p->aMem[1], p->nMem, freebuffers);
sqlite3VdbeFifoClear(&p->sFifo);
if( p->contextStack ){
for(i=0; i<p->contextStackTop; i++){
@@ -1123,7 +1142,7 @@ void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){
Mem *pColName;
int n;
- releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
+ releaseMemArray(p->aColName, p->nResColumn*COLNAME_N, 1);
sqlite3_free(p->aColName);
n = nResColumn*COLNAME_N;
p->nResColumn = nResColumn;
@@ -1648,7 +1667,7 @@ void sqlite3VdbeResetStepResult(Vdbe *p){
** virtual machine from VDBE_MAGIC_RUN or VDBE_MAGIC_HALT back to
** VDBE_MAGIC_INIT.
*/
-int sqlite3VdbeReset(Vdbe *p){
+int sqlite3VdbeReset(Vdbe *p, int freebuffers){
sqlite3 *db;
db = p->db;
@@ -1687,7 +1706,7 @@ int sqlite3VdbeReset(Vdbe *p){
/* Reclaim all memory used by the VDBE
*/
- Cleanup(p);
+ Cleanup(p, freebuffers);
/* Save profiling information from this VDBE run.
*/
@@ -1725,11 +1744,12 @@ int sqlite3VdbeReset(Vdbe *p){
int sqlite3VdbeFinalize(Vdbe *p){
int rc = SQLITE_OK;
if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){
- rc = sqlite3VdbeReset(p);
+ rc = sqlite3VdbeReset(p, 1);
assert( (rc & p->db->errMask)==rc );
}else if( p->magic!=VDBE_MAGIC_INIT ){
return SQLITE_MISUSE;
}
+ releaseMemArray(&p->aMem[1], p->nMem, 1);
sqlite3VdbeDelete(p);
return rc;
}
@@ -1759,7 +1779,7 @@ void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
void sqlite3VdbeDelete(Vdbe *p){
int i;
if( p==0 ) return;
- Cleanup(p);
+ Cleanup(p, 1);
if( p->pPrev ){
p->pPrev->pNext = p->pNext;
}else{
@@ -1779,12 +1799,12 @@ void sqlite3VdbeDelete(Vdbe *p){
}
sqlite3_free(p->aOp);
}
- releaseMemArray(p->aVar, p->nVar);
+ releaseMemArray(p->aVar, p->nVar, 1);
sqlite3_free(p->aLabel);
if( p->aMem ){
sqlite3_free(&p->aMem[1]);
}
- releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
+ releaseMemArray(p->aColName, p->nResColumn*COLNAME_N, 1);
sqlite3_free(p->aColName);
sqlite3_free(p->zSql);
p->magic = VDBE_MAGIC_DEAD;