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:
161
src/vdbeapi.c
161
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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user