/*** 2007 August 14**** The author disclaims copyright to this source code. In place of** a legal notice, here is a blessing:**** May you do good and not evil.** May you find forgiveness for yourself and forgive others.** May you share freely, never taking more than you give.***************************************************************************** This file contains the C functions that implement a memory** allocation subsystem for use by SQLite. **** $Id: mem4.cpp 1282 2008-11-13 09:31:33Z LarsPson $*//*** This version of the memory allocator attempts to obtain memory** from mmap() if the size of the allocation is close to the size** of a virtual memory page. If the size of the allocation is different** from the virtual memory page size, then ordinary malloc() is used.** Ordinary malloc is also used if space allocated to mmap() is** exhausted.**** Enable this memory allocation by compiling with -DSQLITE_MMAP_HEAP_SIZE=nnn** where nnn is the maximum number of bytes of mmap-ed memory you want ** to support. This module may choose to use less memory than requested.***/#if defined(SQLITE_MMAP_HEAP_SIZE)#if defined(SQLITE_MEMDEBUG) || defined(SQLITE_MEMORY_SIZE)# error cannot use SQLITE_MMAP_HEAP_SIZE with either SQLITE_MEMDEBUG \ or SQLITE_MEMORY_SIZE#endif/*** This is a test version of the memory allocator that attempts to** use mmap() and madvise() for allocations and frees of approximately** the virtual memory page size.*/#include <sys/types.h>#include <sys/mman.h>#include <errno.h>#include "sqliteInt.h"#include <unistd.h>/*** All of the static variables used by this module are collected** into a single structure named "mem". This is to keep the** static variables organized and to reduce namespace pollution** when this module is combined with other in the amalgamation.*/static struct { /* ** The alarm callback and its arguments. The mem.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; /* ** Mutex to control access to the memory allocation subsystem. */ sqlite3_mutex *mutex; /* ** Current allocation and high-water mark. */ sqlite3_int64 nowUsed; sqlite3_int64 mxUsed; /* ** Current allocation and high-water marks for mmap allocated memory. */ sqlite3_int64 nowUsedMMap; sqlite3_int64 mxUsedMMap; /* ** Size of a single mmap page. Obtained from sysconf(). */ int szPage; int mnPage; /* ** The number of available mmap pages. */ int nPage; /* ** Index of the first free page. 0 means no pages have been freed. */ int firstFree; /* First unused page on the top of the heap. */ int firstUnused; /* ** Bulk memory obtained from from mmap(). */ char *mmapHeap; /* first byte of the heap */ } mem;/*** Enter the mutex mem.mutex. Allocate it if it is not already allocated.** The mmap() region is initialized the first time this routine is called.*/static void memsys4Enter(void){ if( mem.mutex==0 ){ mem.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM); } sqlite3_mutex_enter(mem.mutex);}/*** Attempt to free memory to the mmap heap. This only works if** the pointer p is within the range of memory addresses that** comprise the mmap heap. Return 1 if the memory was freed** successfully. Return 0 if the pointer is out of range.*/static int mmapFree(void *p){ char *z; int idx, *a; if( mem.mmapHeap==MAP_FAILED || mem.nPage==0 ){ return 0; } z = (char*)p; idx = (z - mem.mmapHeap)/mem.szPage; if( idx<1 || idx>=mem.nPage ){ return 0; } a = (int*)mem.mmapHeap; a[idx] = a[mem.firstFree]; mem.firstFree = idx; mem.nowUsedMMap -= mem.szPage; madvise(p, mem.szPage, MADV_DONTNEED); return 1;}/*** Attempt to allocate nBytes from the mmap heap. Return a pointer** to the allocated page. Or, return NULL if the allocation fails.** ** The allocation will fail if nBytes is not the right size.** Or, the allocation will fail if the mmap heap has been exhausted.*/static void *mmapAlloc(int nBytes){ int idx = 0; if( nBytes>mem.szPage || nBytes<mem.mnPage ){ return 0; } if( mem.nPage==0 ){ mem.szPage = sysconf(_SC_PAGE_SIZE); mem.mnPage = mem.szPage - mem.szPage/10; mem.nPage = SQLITE_MMAP_HEAP_SIZE/mem.szPage; if( mem.nPage * sizeof(int) > mem.szPage ){ mem.nPage = mem.szPage/sizeof(int); } mem.mmapHeap = mmap(0, mem.szPage*mem.nPage, PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_SHARED, -1, 0); if( mem.mmapHeap==MAP_FAILED ){ mem.firstUnused = errno; }else{ mem.firstUnused = 1; mem.nowUsedMMap = mem.szPage; } } if( mem.mmapHeap==MAP_FAILED ){ return 0; } if( mem.firstFree ){ int idx = mem.firstFree; int *a = (int*)mem.mmapHeap; mem.firstFree = a[idx]; }else if( mem.firstUnused<mem.nPage ){ idx = mem.firstUnused++; } if( idx ){ mem.nowUsedMMap += mem.szPage; if( mem.nowUsedMMap>mem.mxUsedMMap ){ mem.mxUsedMMap = mem.nowUsedMMap; } return (void*)&mem.mmapHeap[idx*mem.szPage]; }else{ return 0; }}/*** Release the mmap-ed memory region if it is currently allocated and** is not in use.*/static void mmapUnmap(void){ if( mem.mmapHeap==MAP_FAILED ) return; if( mem.nPage==0 ) return; if( mem.nowUsedMMap>mem.szPage ) return; munmap(mem.mmapHeap, mem.nPage*mem.szPage); mem.nowUsedMMap = 0; mem.nPage = 0;}/*** Return the amount of memory currently checked out.*/sqlite3_int64 sqlite3_memory_used(void){ sqlite3_int64 n; memsys4Enter(); n = mem.nowUsed + mem.nowUsedMMap; sqlite3_mutex_leave(mem.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; memsys4Enter(); n = mem.mxUsed + mem.mxUsedMMap; if( resetFlag ){ mem.mxUsed = mem.nowUsed; mem.mxUsedMMap = mem.nowUsedMMap; } sqlite3_mutex_leave(mem.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){ memsys4Enter(); mem.alarmCallback = xCallback; mem.alarmArg = pArg; mem.alarmThreshold = iThreshold; sqlite3_mutex_leave(mem.mutex); return SQLITE_OK;}/*** Trigger the alarm */static void sqlite3MemsysAlarm(int nByte){ void (*xCallback)(void*,sqlite3_int64,int); sqlite3_int64 nowUsed; void *pArg; if( mem.alarmCallback==0 || mem.alarmBusy ) return; mem.alarmBusy = 1; xCallback = mem.alarmCallback; nowUsed = mem.nowUsed; pArg = mem.alarmArg; sqlite3_mutex_leave(mem.mutex); xCallback(pArg, nowUsed, nByte); sqlite3_mutex_enter(mem.mutex); mem.alarmBusy = 0;}/*** Allocate nBytes of memory*/static void *memsys4Malloc(int nBytes){ sqlite3_int64 *p = 0; if( mem.alarmCallback!=0 && mem.nowUsed+mem.nowUsedMMap+nBytes>=mem.alarmThreshold ){ sqlite3MemsysAlarm(nBytes); } if( (p = mmapAlloc(nBytes))==0 ){ p = malloc(nBytes+8); if( p==0 ){ sqlite3MemsysAlarm(nBytes); p = malloc(nBytes+8); } if( p ){ p[0] = nBytes; p++; mem.nowUsed += nBytes; if( mem.nowUsed>mem.mxUsed ){ mem.mxUsed = mem.nowUsed; } } } return (void*)p; }/*** Return the size of a memory allocation*/static int memsys4Size(void *pPrior){ char *z = (char*)pPrior; int idx = mem.nPage ? (z - mem.mmapHeap)/mem.szPage : 0; int nByte; if( idx>=1 && idx<mem.nPage ){ nByte = mem.szPage; }else{ sqlite3_int64 *p = pPrior; p--; nByte = (int)*p; } return nByte;}/*** Free memory.*/static void memsys4Free(void *pPrior){ sqlite3_int64 *p; int nByte; if( mmapFree(pPrior)==0 ){ p = pPrior; p--; nByte = (int)*p; mem.nowUsed -= nByte; free(p); if( mem.nowUsed==0 ){ mmapUnmap(); } }}/*** Allocate nBytes of memory*/void *sqlite3_malloc(int nBytes){ sqlite3_int64 *p = 0; if( nBytes>0 ){ memsys4Enter(); p = memsys4Malloc(nBytes); sqlite3_mutex_leave(mem.mutex); } return (void*)p; }/*** Free memory.*/void sqlite3_free(void *pPrior){ if( pPrior==0 ){ return; } assert( mem.mutex!=0 ); sqlite3_mutex_enter(mem.mutex); memsys4Free(pPrior); sqlite3_mutex_leave(mem.mutex); }/*** Change the size of an existing memory allocation*/void *sqlite3_realloc(void *pPrior, int nBytes){ int nOld; sqlite3_int64 *p; if( pPrior==0 ){ return sqlite3_malloc(nBytes); } if( nBytes<=0 ){ sqlite3_free(pPrior); return 0; } nOld = memsys4Size(pPrior); if( nBytes<=nOld && nBytes>=nOld-128 ){ return pPrior; } assert( mem.mutex!=0 ); sqlite3_mutex_enter(mem.mutex); p = memsys4Malloc(nBytes); if( p ){ if( nOld<nBytes ){ memcpy(p, pPrior, nOld); }else{ memcpy(p, pPrior, nBytes); } memsys4Free(pPrior); } assert( mem.mutex!=0 ); sqlite3_mutex_leave(mem.mutex); return (void*)p;}#endif /* !SQLITE_MEMDEBUG && !SQLITE_OMIT_MEMORY_ALLOCATION */