1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

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
This commit is contained in:
danielk1977
2008-03-26 18:34:43 +00:00
parent 7e2e6dc950
commit dfb316d432
12 changed files with 234 additions and 45 deletions

View File

@@ -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;
}