dvander@1645: /* dvander@1645: ** 2007 August 14 dvander@1645: ** dvander@1645: ** The author disclaims copyright to this source code. In place of dvander@1645: ** a legal notice, here is a blessing: dvander@1645: ** dvander@1645: ** May you do good and not evil. dvander@1645: ** May you find forgiveness for yourself and forgive others. dvander@1645: ** May you share freely, never taking more than you give. dvander@1645: ** dvander@1645: ************************************************************************* dvander@1645: ** This file contains the C functions that implement a memory dvander@1645: ** allocation subsystem for use by SQLite. dvander@1645: ** dvander@1645: ** $Id$ dvander@1645: */ dvander@1645: dvander@1645: /* dvander@1645: ** This version of the memory allocator is the default. It is dvander@1645: ** used when no other memory allocator is specified using compile-time dvander@1645: ** macros. dvander@1645: */ dvander@1645: #if !defined(SQLITE_MEMDEBUG) && !defined(SQLITE_OMIT_MEMORY_ALLOCATION) dvander@1645: dvander@1645: /* dvander@1645: ** We will eventually construct multiple memory allocation subsystems dvander@1645: ** suitable for use in various contexts: dvander@1645: ** dvander@1645: ** * Normal multi-threaded builds dvander@1645: ** * Normal single-threaded builds dvander@1645: ** * Debugging builds dvander@1645: ** dvander@1645: ** This initial version is suitable for use in normal multi-threaded dvander@1645: ** builds. We envision that alternative versions will be stored in dvander@1645: ** separate source files. #ifdefs will be used to select the code from dvander@1645: ** one of the various memN.c source files for use in any given build. dvander@1645: */ dvander@1645: #include "sqliteInt.h" dvander@1645: dvander@1645: /* dvander@1645: ** All of the static variables used by this module are collected dvander@1645: ** into a single structure named "mem". This is to keep the dvander@1645: ** static variables organized and to reduce namespace pollution dvander@1645: ** when this module is combined with other in the amalgamation. dvander@1645: */ dvander@1645: static struct { dvander@1645: /* dvander@1645: ** The alarm callback and its arguments. The mem.mutex lock will dvander@1645: ** be held while the callback is running. Recursive calls into dvander@1645: ** the memory subsystem are allowed, but no new callbacks will be dvander@1645: ** issued. The alarmBusy variable is set to prevent recursive dvander@1645: ** callbacks. dvander@1645: */ dvander@1645: sqlite3_int64 alarmThreshold; dvander@1645: void (*alarmCallback)(void*, sqlite3_int64,int); dvander@1645: void *alarmArg; dvander@1645: int alarmBusy; dvander@1645: dvander@1645: /* dvander@1645: ** Mutex to control access to the memory allocation subsystem. dvander@1645: */ dvander@1645: sqlite3_mutex *mutex; dvander@1645: dvander@1645: /* dvander@1645: ** Current allocation and high-water mark. dvander@1645: */ dvander@1645: sqlite3_int64 nowUsed; dvander@1645: sqlite3_int64 mxUsed; dvander@1645: dvander@1645: dvander@1645: } mem; dvander@1645: dvander@1645: /* dvander@1645: ** Enter the mutex mem.mutex. Allocate it if it is not already allocated. dvander@1645: */ dvander@1645: static void enterMem(void){ dvander@1645: if( mem.mutex==0 ){ dvander@1645: mem.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM); dvander@1645: } dvander@1645: sqlite3_mutex_enter(mem.mutex); dvander@1645: } dvander@1645: dvander@1645: /* dvander@1645: ** Return the amount of memory currently checked out. dvander@1645: */ dvander@1645: sqlite3_int64 sqlite3_memory_used(void){ dvander@1645: sqlite3_int64 n; dvander@1645: enterMem(); dvander@1645: n = mem.nowUsed; dvander@1645: sqlite3_mutex_leave(mem.mutex); dvander@1645: return n; dvander@1645: } dvander@1645: dvander@1645: /* dvander@1645: ** Return the maximum amount of memory that has ever been dvander@1645: ** checked out since either the beginning of this process dvander@1645: ** or since the most recent reset. dvander@1645: */ dvander@1645: sqlite3_int64 sqlite3_memory_highwater(int resetFlag){ dvander@1645: sqlite3_int64 n; dvander@1645: enterMem(); dvander@1645: n = mem.mxUsed; dvander@1645: if( resetFlag ){ dvander@1645: mem.mxUsed = mem.nowUsed; dvander@1645: } dvander@1645: sqlite3_mutex_leave(mem.mutex); dvander@1645: return n; dvander@1645: } dvander@1645: dvander@1645: /* dvander@1645: ** Change the alarm callback dvander@1645: */ dvander@1645: int sqlite3_memory_alarm( dvander@1645: void(*xCallback)(void *pArg, sqlite3_int64 used,int N), dvander@1645: void *pArg, dvander@1645: sqlite3_int64 iThreshold dvander@1645: ){ dvander@1645: enterMem(); dvander@1645: mem.alarmCallback = xCallback; dvander@1645: mem.alarmArg = pArg; dvander@1645: mem.alarmThreshold = iThreshold; dvander@1645: sqlite3_mutex_leave(mem.mutex); dvander@1645: return SQLITE_OK; dvander@1645: } dvander@1645: dvander@1645: /* dvander@1645: ** Trigger the alarm dvander@1645: */ dvander@1645: static void sqlite3MemsysAlarm(int nByte){ dvander@1645: void (*xCallback)(void*,sqlite3_int64,int); dvander@1645: sqlite3_int64 nowUsed; dvander@1645: void *pArg; dvander@1645: if( mem.alarmCallback==0 || mem.alarmBusy ) return; dvander@1645: mem.alarmBusy = 1; dvander@1645: xCallback = mem.alarmCallback; dvander@1645: nowUsed = mem.nowUsed; dvander@1645: pArg = mem.alarmArg; dvander@1645: sqlite3_mutex_leave(mem.mutex); dvander@1645: xCallback(pArg, nowUsed, nByte); dvander@1645: sqlite3_mutex_enter(mem.mutex); dvander@1645: mem.alarmBusy = 0; dvander@1645: } dvander@1645: dvander@1645: /* dvander@1645: ** Allocate nBytes of memory dvander@1645: */ dvander@1645: void *sqlite3_malloc(int nBytes){ dvander@1645: sqlite3_int64 *p = 0; dvander@1645: if( nBytes>0 ){ dvander@1645: enterMem(); dvander@1645: if( mem.alarmCallback!=0 && mem.nowUsed+nBytes>=mem.alarmThreshold ){ dvander@1645: sqlite3MemsysAlarm(nBytes); dvander@1645: } dvander@1645: p = malloc(nBytes+8); dvander@1645: if( p==0 ){ dvander@1645: sqlite3MemsysAlarm(nBytes); dvander@1645: p = malloc(nBytes+8); dvander@1645: } dvander@1645: if( p ){ dvander@1645: p[0] = nBytes; dvander@1645: p++; dvander@1645: mem.nowUsed += nBytes; dvander@1645: if( mem.nowUsed>mem.mxUsed ){ dvander@1645: mem.mxUsed = mem.nowUsed; dvander@1645: } dvander@1645: } dvander@1645: sqlite3_mutex_leave(mem.mutex); dvander@1645: } dvander@1645: return (void*)p; dvander@1645: } dvander@1645: dvander@1645: /* dvander@1645: ** Free memory. dvander@1645: */ dvander@1645: void sqlite3_free(void *pPrior){ dvander@1645: sqlite3_int64 *p; dvander@1645: int nByte; dvander@1645: if( pPrior==0 ){ dvander@1645: return; dvander@1645: } dvander@1645: assert( mem.mutex!=0 ); dvander@1645: p = pPrior; dvander@1645: p--; dvander@1645: nByte = (int)*p; dvander@1645: sqlite3_mutex_enter(mem.mutex); dvander@1645: mem.nowUsed -= nByte; dvander@1645: free(p); dvander@1645: sqlite3_mutex_leave(mem.mutex); dvander@1645: } dvander@1645: dvander@1645: /* dvander@1645: ** Change the size of an existing memory allocation dvander@1645: */ dvander@1645: void *sqlite3_realloc(void *pPrior, int nBytes){ dvander@1645: int nOld; dvander@1645: sqlite3_int64 *p; dvander@1645: if( pPrior==0 ){ dvander@1645: return sqlite3_malloc(nBytes); dvander@1645: } dvander@1645: if( nBytes<=0 ){ dvander@1645: sqlite3_free(pPrior); dvander@1645: return 0; dvander@1645: } dvander@1645: p = pPrior; dvander@1645: p--; dvander@1645: nOld = (int)p[0]; dvander@1645: assert( mem.mutex!=0 ); dvander@1645: sqlite3_mutex_enter(mem.mutex); dvander@1645: if( mem.nowUsed+nBytes-nOld>=mem.alarmThreshold ){ dvander@1645: sqlite3MemsysAlarm(nBytes-nOld); dvander@1645: } dvander@1645: p = realloc(p, nBytes+8); dvander@1645: if( p==0 ){ dvander@1645: sqlite3MemsysAlarm(nBytes); dvander@1645: p = realloc(p, nBytes+8); dvander@1645: } dvander@1645: if( p ){ dvander@1645: p[0] = nBytes; dvander@1645: p++; dvander@1645: mem.nowUsed += nBytes-nOld; dvander@1645: if( mem.nowUsed>mem.mxUsed ){ dvander@1645: mem.mxUsed = mem.nowUsed; dvander@1645: } dvander@1645: } dvander@1645: sqlite3_mutex_leave(mem.mutex); dvander@1645: return (void*)p; dvander@1645: } dvander@1645: dvander@1645: #endif /* !SQLITE_MEMDEBUG && !SQLITE_OMIT_MEMORY_ALLOCATION */