Continuing progress on the new memory allocation subsystem.  Added the
sqlite3_mem_methods structure for defining new memory allocators at
run-time. (CVS 5219)

FossilOrigin-Name: f00305f4cd2f487f660f34a21c1c24a0b37c7275
diff --git a/src/malloc.c b/src/malloc.c
index 55d5c76..5916ec2 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -9,10 +9,10 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
+**
 ** Memory allocation functions used throughout sqlite.
 **
-**
-** $Id: malloc.c,v 1.15 2008/03/26 18:34:43 danielk1977 Exp $
+** $Id: malloc.c,v 1.16 2008/06/14 16:56:22 drh Exp $
 */
 #include "sqliteInt.h"
 #include <stdarg.h>
@@ -67,12 +67,252 @@
 #endif
 }
 
+/*
+** State information local to the memory allocation subsystem.
+*/
+static struct {
+  sqlite3_mutex *mutex;         /* Mutex to serialize access */
+
+  /*
+  ** The alarm callback and its arguments.  The mem0.mutex lock will
+  ** be held while the callback is running.  Recursive calls into
+  ** the memory subsystem are allowed, but no new callbacks will be
+  ** issued.  The alarmBusy variable is set to prevent recursive
+  ** callbacks.
+  */
+  sqlite3_int64 alarmThreshold;
+  void (*alarmCallback)(void*, sqlite3_int64,int);
+  void *alarmArg;
+  int alarmBusy;
+
+  /*
+  ** Performance statistics
+  */
+  sqlite3_int64 nowUsed;  /* Main memory currently in use */
+  sqlite3_int64 mxUsed;   /* Highwater mark for nowUsed */
+  int mxReq;              /* maximum request size for main or page-cache mem */
+} mem0;
+
+/*
+** Initialize the memory allocation subsystem.
+*/
+int sqlite3MallocInit(void){
+  if( sqlite3Config.m.xMalloc==0 ){
+    sqlite3MemSetDefault();
+  }
+  memset(&mem0, 0, sizeof(mem0));
+  if( sqlite3Config.bMemstat && sqlite3Config.bCoreMutex ){
+    mem0.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM);
+  }
+  return sqlite3Config.m.xInit(sqlite3Config.m.pAppData);
+}
+
+/*
+** Deinitialize the memory allocation subsystem.
+*/
+void sqlite3MallocEnd(void){
+   sqlite3Config.m.xShutdown(sqlite3Config.m.pAppData);
+}
+
+/*
+** Return the amount of memory currently checked out.
+*/
+sqlite3_int64 sqlite3_memory_used(void){
+  sqlite3_int64 n;
+  sqlite3_mutex_enter(mem0.mutex);
+  n = mem0.nowUsed;
+  sqlite3_mutex_leave(mem0.mutex);  
+  return n;
+}
+
+/*
+** 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;
+  sqlite3_mutex_enter(mem0.mutex);
+  n = mem0.mxUsed;
+  if( resetFlag ){
+    mem0.mxUsed = mem0.nowUsed;
+  }
+  sqlite3_mutex_leave(mem0.mutex);  
+  return n;
+}
+
+/*
+** Change the alarm callback
+*/
+int sqlite3_memory_alarm(
+  void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
+  void *pArg,
+  sqlite3_int64 iThreshold
+){
+  sqlite3_mutex_enter(mem0.mutex);
+  mem0.alarmCallback = xCallback;
+  mem0.alarmArg = pArg;
+  mem0.alarmThreshold = iThreshold;
+  sqlite3_mutex_leave(mem0.mutex);
+  return SQLITE_OK;
+}
+
+/*
+** Trigger the alarm 
+*/
+static void sqlite3MallocAlarm(int nByte){
+  void (*xCallback)(void*,sqlite3_int64,int);
+  sqlite3_int64 nowUsed;
+  void *pArg;
+  if( mem0.alarmCallback==0 || mem0.alarmBusy  ) return;
+  mem0.alarmBusy = 1;
+  xCallback = mem0.alarmCallback;
+  nowUsed = mem0.nowUsed;
+  pArg = mem0.alarmArg;
+  sqlite3_mutex_leave(mem0.mutex);
+  xCallback(pArg, nowUsed, nByte);
+  sqlite3_mutex_enter(mem0.mutex);
+  mem0.alarmBusy = 0;
+}
+
+
+/*
+** Allocate memory.  This routine is like sqlite3_malloc() except that it
+** assumes the memory subsystem has already been initialized.
+*/
+void *sqlite3Malloc(int n){
+  void *p;
+  int nFull;
+  if( n<=0 ){
+    return 0;
+  }else if( sqlite3Config.bMemstat ){
+    nFull = sqlite3Config.m.xRoundup(n);
+    sqlite3_mutex_enter(mem0.mutex);
+    if( n>mem0.mxReq ) mem0.mxReq = n;
+    if( mem0.alarmCallback!=0 && mem0.nowUsed+nFull>=mem0.alarmThreshold ){
+      sqlite3MallocAlarm(nFull);
+    }
+    if( sqlite3FaultStep(SQLITE_FAULTINJECTOR_MALLOC) ){
+      p = 0;
+    }else{
+      p = sqlite3Config.m.xMalloc(nFull);
+      if( p==0 ){
+        sqlite3MallocAlarm(nFull);
+        p = malloc(nFull);
+      }
+    }
+    if( p ){
+      mem0.nowUsed += nFull;
+      if( mem0.nowUsed>mem0.mxUsed ){
+        mem0.mxUsed = mem0.nowUsed;
+      }
+    }
+    sqlite3_mutex_leave(mem0.mutex);
+  }else{
+    p = sqlite3Config.m.xMalloc(n);
+  }
+  return p;
+}
+
+/*
+** This version of the memory allocation is for use by the application.
+** First make sure the memory subsystem is initialized, then do the
+** allocation.
+*/
+void *sqlite3_malloc(int n){
+#ifndef SQLITE_OMIT_AUTOINIT
+  if( sqlite3_initialize() ) return 0;
+#endif
+  return sqlite3Malloc(n);
+}
+
+/*
+** Return the size of a memory allocation previously obtained from
+** sqlite3Malloc() or sqlite3_malloc().
+*/
+int sqlite3MallocSize(void *p){
+  return sqlite3Config.m.xSize(p);
+}
+
+/*
+** Free memory previously obtained from sqlite3Malloc().
+*/
+void sqlite3_free(void *p){
+  if( p==0 ) return;
+  if( sqlite3Config.bMemstat ){
+    sqlite3_mutex_enter(mem0.mutex);
+    mem0.nowUsed -= sqlite3MallocSize(p);
+    sqlite3Config.m.xFree(p);
+    sqlite3_mutex_leave(mem0.mutex);
+  }else{
+    sqlite3Config.m.xFree(p);
+  }
+}
+
+/*
+** Change the size of an existing memory allocation
+*/
+void *sqlite3Realloc(void *pOld, int nBytes){
+  int nOld, nNew;
+  void *pNew;
+  if( pOld==0 ){
+    return sqlite3Malloc(nBytes);
+  }
+  if( nBytes<=0 ){
+    sqlite3_free(pOld);
+    return 0;
+  }
+  nOld = sqlite3MallocSize(pOld);
+  if( sqlite3Config.bMemstat ){
+    sqlite3_mutex_enter(mem0.mutex);
+    if( nBytes>mem0.mxReq ) mem0.mxReq = nBytes;
+    nNew = sqlite3Config.m.xRoundup(nBytes);
+    if( nOld==nNew ){
+      pNew = pOld;
+    }else{
+      if( mem0.nowUsed+nNew-nOld>=mem0.alarmThreshold ){
+        sqlite3MallocAlarm(nNew-nOld);
+      }
+      if( sqlite3FaultStep(SQLITE_FAULTINJECTOR_MALLOC) ){
+        pNew = 0;
+      }else{
+        pNew = sqlite3Config.m.xRealloc(pOld, nNew);
+        if( pNew==0 ){
+          sqlite3MallocAlarm(nBytes);
+          pNew = sqlite3Config.m.xRealloc(pOld, nNew);
+        }
+      }
+      if( pNew ){
+        mem0.nowUsed += nNew-nOld;
+        if( mem0.nowUsed>mem0.mxUsed ){
+          mem0.mxUsed = mem0.nowUsed;
+        }
+      }
+    }
+    sqlite3_mutex_leave(mem0.mutex);
+  }else{
+    pNew = sqlite3Config.m.xRealloc(pOld, nBytes);
+  }
+  return pNew;
+}
+
+/*
+** The public interface to sqlite3Realloc.  Make sure that the memory
+** subsystem is initialized prior to invoking sqliteRealloc.
+*/
+void *sqlite3_realloc(void *pOld, int n){
+#ifndef SQLITE_OMIT_AUTOINIT
+  if( sqlite3_initialize() ) return 0;
+#endif
+  return sqlite3Realloc(pOld, n);
+}
+
 
 /*
 ** Allocate and zero memory.
 */ 
-void *sqlite3MallocZero(unsigned n){
-  void *p = sqlite3_malloc(n);
+void *sqlite3MallocZero(int n){
+  void *p = sqlite3Malloc(n);
   if( p ){
     memset(p, 0, n);
   }
@@ -83,7 +323,7 @@
 ** Allocate and zero memory.  If the allocation fails, make
 ** the mallocFailed flag in the connection pointer.
 */
-void *sqlite3DbMallocZero(sqlite3 *db, unsigned n){
+void *sqlite3DbMallocZero(sqlite3 *db, int n){
   void *p = sqlite3DbMallocRaw(db, n);
   if( p ){
     memset(p, 0, n);
@@ -95,10 +335,10 @@
 ** Allocate and zero memory.  If the allocation fails, make
 ** the mallocFailed flag in the connection pointer.
 */
-void *sqlite3DbMallocRaw(sqlite3 *db, unsigned n){
+void *sqlite3DbMallocRaw(sqlite3 *db, int n){
   void *p = 0;
   if( !db || db->mallocFailed==0 ){
-    p = sqlite3_malloc(n);
+    p = sqlite3Malloc(n);
     if( !p && db ){
       db->mallocFailed = 1;
     }