summaryrefslogtreecommitdiffstats
path: root/third_party/sqlite/src/mem4.c
blob: 5a7809856196363cf6948edc901f611c62ec175b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/*
** 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.c,v 1.3 2008/06/18 17:09:10 danielk1977 Exp $
*/
#include "sqliteInt.h"

/*
** 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.
**
*/
#ifdef SQLITE_MMAP_HEAP_SIZE

/*
** 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 <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 = sqlite3MutexAlloc(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_MMAP_HEAP_SIZE */