|
1 /* |
|
2 ** 2007 August 14 |
|
3 ** |
|
4 ** The author disclaims copyright to this source code. In place of |
|
5 ** a legal notice, here is a blessing: |
|
6 ** |
|
7 ** May you do good and not evil. |
|
8 ** May you find forgiveness for yourself and forgive others. |
|
9 ** May you share freely, never taking more than you give. |
|
10 ** |
|
11 ************************************************************************* |
|
12 ** This file contains the C functions that implement a memory |
|
13 ** allocation subsystem for use by SQLite. |
|
14 ** |
|
15 ** $Id: mem4.c,v 1.3 2008/06/18 17:09:10 danielk1977 Exp $ |
|
16 */ |
|
17 #include "sqliteInt.h" |
|
18 |
|
19 /* |
|
20 ** This version of the memory allocator attempts to obtain memory |
|
21 ** from mmap() if the size of the allocation is close to the size |
|
22 ** of a virtual memory page. If the size of the allocation is different |
|
23 ** from the virtual memory page size, then ordinary malloc() is used. |
|
24 ** Ordinary malloc is also used if space allocated to mmap() is |
|
25 ** exhausted. |
|
26 ** |
|
27 ** Enable this memory allocation by compiling with -DSQLITE_MMAP_HEAP_SIZE=nnn |
|
28 ** where nnn is the maximum number of bytes of mmap-ed memory you want |
|
29 ** to support. This module may choose to use less memory than requested. |
|
30 ** |
|
31 */ |
|
32 #ifdef SQLITE_MMAP_HEAP_SIZE |
|
33 |
|
34 /* |
|
35 ** This is a test version of the memory allocator that attempts to |
|
36 ** use mmap() and madvise() for allocations and frees of approximately |
|
37 ** the virtual memory page size. |
|
38 */ |
|
39 #include <sys/types.h> |
|
40 #include <sys/mman.h> |
|
41 #include <errno.h> |
|
42 #include <unistd.h> |
|
43 |
|
44 |
|
45 /* |
|
46 ** All of the static variables used by this module are collected |
|
47 ** into a single structure named "mem". This is to keep the |
|
48 ** static variables organized and to reduce namespace pollution |
|
49 ** when this module is combined with other in the amalgamation. |
|
50 */ |
|
51 static struct { |
|
52 /* |
|
53 ** The alarm callback and its arguments. The mem.mutex lock will |
|
54 ** be held while the callback is running. Recursive calls into |
|
55 ** the memory subsystem are allowed, but no new callbacks will be |
|
56 ** issued. The alarmBusy variable is set to prevent recursive |
|
57 ** callbacks. |
|
58 */ |
|
59 sqlite3_int64 alarmThreshold; |
|
60 void (*alarmCallback)(void*, sqlite3_int64,int); |
|
61 void *alarmArg; |
|
62 int alarmBusy; |
|
63 |
|
64 /* |
|
65 ** Mutex to control access to the memory allocation subsystem. |
|
66 */ |
|
67 sqlite3_mutex *mutex; |
|
68 |
|
69 /* |
|
70 ** Current allocation and high-water mark. |
|
71 */ |
|
72 sqlite3_int64 nowUsed; |
|
73 sqlite3_int64 mxUsed; |
|
74 |
|
75 /* |
|
76 ** Current allocation and high-water marks for mmap allocated memory. |
|
77 */ |
|
78 sqlite3_int64 nowUsedMMap; |
|
79 sqlite3_int64 mxUsedMMap; |
|
80 |
|
81 /* |
|
82 ** Size of a single mmap page. Obtained from sysconf(). |
|
83 */ |
|
84 int szPage; |
|
85 int mnPage; |
|
86 |
|
87 /* |
|
88 ** The number of available mmap pages. |
|
89 */ |
|
90 int nPage; |
|
91 |
|
92 /* |
|
93 ** Index of the first free page. 0 means no pages have been freed. |
|
94 */ |
|
95 int firstFree; |
|
96 |
|
97 /* First unused page on the top of the heap. |
|
98 */ |
|
99 int firstUnused; |
|
100 |
|
101 /* |
|
102 ** Bulk memory obtained from from mmap(). |
|
103 */ |
|
104 char *mmapHeap; /* first byte of the heap */ |
|
105 |
|
106 } mem; |
|
107 |
|
108 |
|
109 /* |
|
110 ** Enter the mutex mem.mutex. Allocate it if it is not already allocated. |
|
111 ** The mmap() region is initialized the first time this routine is called. |
|
112 */ |
|
113 static void memsys4Enter(void){ |
|
114 if( mem.mutex==0 ){ |
|
115 mem.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); |
|
116 } |
|
117 sqlite3_mutex_enter(mem.mutex); |
|
118 } |
|
119 |
|
120 /* |
|
121 ** Attempt to free memory to the mmap heap. This only works if |
|
122 ** the pointer p is within the range of memory addresses that |
|
123 ** comprise the mmap heap. Return 1 if the memory was freed |
|
124 ** successfully. Return 0 if the pointer is out of range. |
|
125 */ |
|
126 static int mmapFree(void *p){ |
|
127 char *z; |
|
128 int idx, *a; |
|
129 if( mem.mmapHeap==MAP_FAILED || mem.nPage==0 ){ |
|
130 return 0; |
|
131 } |
|
132 z = (char*)p; |
|
133 idx = (z - mem.mmapHeap)/mem.szPage; |
|
134 if( idx<1 || idx>=mem.nPage ){ |
|
135 return 0; |
|
136 } |
|
137 a = (int*)mem.mmapHeap; |
|
138 a[idx] = a[mem.firstFree]; |
|
139 mem.firstFree = idx; |
|
140 mem.nowUsedMMap -= mem.szPage; |
|
141 madvise(p, mem.szPage, MADV_DONTNEED); |
|
142 return 1; |
|
143 } |
|
144 |
|
145 /* |
|
146 ** Attempt to allocate nBytes from the mmap heap. Return a pointer |
|
147 ** to the allocated page. Or, return NULL if the allocation fails. |
|
148 ** |
|
149 ** The allocation will fail if nBytes is not the right size. |
|
150 ** Or, the allocation will fail if the mmap heap has been exhausted. |
|
151 */ |
|
152 static void *mmapAlloc(int nBytes){ |
|
153 int idx = 0; |
|
154 if( nBytes>mem.szPage || nBytes<mem.mnPage ){ |
|
155 return 0; |
|
156 } |
|
157 if( mem.nPage==0 ){ |
|
158 mem.szPage = sysconf(_SC_PAGE_SIZE); |
|
159 mem.mnPage = mem.szPage - mem.szPage/10; |
|
160 mem.nPage = SQLITE_MMAP_HEAP_SIZE/mem.szPage; |
|
161 if( mem.nPage * sizeof(int) > mem.szPage ){ |
|
162 mem.nPage = mem.szPage/sizeof(int); |
|
163 } |
|
164 mem.mmapHeap = mmap(0, mem.szPage*mem.nPage, PROT_WRITE|PROT_READ, |
|
165 MAP_ANONYMOUS|MAP_SHARED, -1, 0); |
|
166 if( mem.mmapHeap==MAP_FAILED ){ |
|
167 mem.firstUnused = errno; |
|
168 }else{ |
|
169 mem.firstUnused = 1; |
|
170 mem.nowUsedMMap = mem.szPage; |
|
171 } |
|
172 } |
|
173 if( mem.mmapHeap==MAP_FAILED ){ |
|
174 return 0; |
|
175 } |
|
176 if( mem.firstFree ){ |
|
177 int idx = mem.firstFree; |
|
178 int *a = (int*)mem.mmapHeap; |
|
179 mem.firstFree = a[idx]; |
|
180 }else if( mem.firstUnused<mem.nPage ){ |
|
181 idx = mem.firstUnused++; |
|
182 } |
|
183 if( idx ){ |
|
184 mem.nowUsedMMap += mem.szPage; |
|
185 if( mem.nowUsedMMap>mem.mxUsedMMap ){ |
|
186 mem.mxUsedMMap = mem.nowUsedMMap; |
|
187 } |
|
188 return (void*)&mem.mmapHeap[idx*mem.szPage]; |
|
189 }else{ |
|
190 return 0; |
|
191 } |
|
192 } |
|
193 |
|
194 /* |
|
195 ** Release the mmap-ed memory region if it is currently allocated and |
|
196 ** is not in use. |
|
197 */ |
|
198 static void mmapUnmap(void){ |
|
199 if( mem.mmapHeap==MAP_FAILED ) return; |
|
200 if( mem.nPage==0 ) return; |
|
201 if( mem.nowUsedMMap>mem.szPage ) return; |
|
202 munmap(mem.mmapHeap, mem.nPage*mem.szPage); |
|
203 mem.nowUsedMMap = 0; |
|
204 mem.nPage = 0; |
|
205 } |
|
206 |
|
207 |
|
208 /* |
|
209 ** Return the amount of memory currently checked out. |
|
210 */ |
|
211 sqlite3_int64 sqlite3_memory_used(void){ |
|
212 sqlite3_int64 n; |
|
213 memsys4Enter(); |
|
214 n = mem.nowUsed + mem.nowUsedMMap; |
|
215 sqlite3_mutex_leave(mem.mutex); |
|
216 return n; |
|
217 } |
|
218 |
|
219 /* |
|
220 ** Return the maximum amount of memory that has ever been |
|
221 ** checked out since either the beginning of this process |
|
222 ** or since the most recent reset. |
|
223 */ |
|
224 sqlite3_int64 sqlite3_memory_highwater(int resetFlag){ |
|
225 sqlite3_int64 n; |
|
226 memsys4Enter(); |
|
227 n = mem.mxUsed + mem.mxUsedMMap; |
|
228 if( resetFlag ){ |
|
229 mem.mxUsed = mem.nowUsed; |
|
230 mem.mxUsedMMap = mem.nowUsedMMap; |
|
231 } |
|
232 sqlite3_mutex_leave(mem.mutex); |
|
233 return n; |
|
234 } |
|
235 |
|
236 /* |
|
237 ** Change the alarm callback |
|
238 */ |
|
239 int sqlite3_memory_alarm( |
|
240 void(*xCallback)(void *pArg, sqlite3_int64 used,int N), |
|
241 void *pArg, |
|
242 sqlite3_int64 iThreshold |
|
243 ){ |
|
244 memsys4Enter(); |
|
245 mem.alarmCallback = xCallback; |
|
246 mem.alarmArg = pArg; |
|
247 mem.alarmThreshold = iThreshold; |
|
248 sqlite3_mutex_leave(mem.mutex); |
|
249 return SQLITE_OK; |
|
250 } |
|
251 |
|
252 /* |
|
253 ** Trigger the alarm |
|
254 */ |
|
255 static void sqlite3MemsysAlarm(int nByte){ |
|
256 void (*xCallback)(void*,sqlite3_int64,int); |
|
257 sqlite3_int64 nowUsed; |
|
258 void *pArg; |
|
259 if( mem.alarmCallback==0 || mem.alarmBusy ) return; |
|
260 mem.alarmBusy = 1; |
|
261 xCallback = mem.alarmCallback; |
|
262 nowUsed = mem.nowUsed; |
|
263 pArg = mem.alarmArg; |
|
264 sqlite3_mutex_leave(mem.mutex); |
|
265 xCallback(pArg, nowUsed, nByte); |
|
266 sqlite3_mutex_enter(mem.mutex); |
|
267 mem.alarmBusy = 0; |
|
268 } |
|
269 |
|
270 /* |
|
271 ** Allocate nBytes of memory |
|
272 */ |
|
273 static void *memsys4Malloc(int nBytes){ |
|
274 sqlite3_int64 *p = 0; |
|
275 if( mem.alarmCallback!=0 |
|
276 && mem.nowUsed+mem.nowUsedMMap+nBytes>=mem.alarmThreshold ){ |
|
277 sqlite3MemsysAlarm(nBytes); |
|
278 } |
|
279 if( (p = mmapAlloc(nBytes))==0 ){ |
|
280 p = malloc(nBytes+8); |
|
281 if( p==0 ){ |
|
282 sqlite3MemsysAlarm(nBytes); |
|
283 p = malloc(nBytes+8); |
|
284 } |
|
285 if( p ){ |
|
286 p[0] = nBytes; |
|
287 p++; |
|
288 mem.nowUsed += nBytes; |
|
289 if( mem.nowUsed>mem.mxUsed ){ |
|
290 mem.mxUsed = mem.nowUsed; |
|
291 } |
|
292 } |
|
293 } |
|
294 return (void*)p; |
|
295 } |
|
296 |
|
297 /* |
|
298 ** Return the size of a memory allocation |
|
299 */ |
|
300 static int memsys4Size(void *pPrior){ |
|
301 char *z = (char*)pPrior; |
|
302 int idx = mem.nPage ? (z - mem.mmapHeap)/mem.szPage : 0; |
|
303 int nByte; |
|
304 if( idx>=1 && idx<mem.nPage ){ |
|
305 nByte = mem.szPage; |
|
306 }else{ |
|
307 sqlite3_int64 *p = pPrior; |
|
308 p--; |
|
309 nByte = (int)*p; |
|
310 } |
|
311 return nByte; |
|
312 } |
|
313 |
|
314 /* |
|
315 ** Free memory. |
|
316 */ |
|
317 static void memsys4Free(void *pPrior){ |
|
318 sqlite3_int64 *p; |
|
319 int nByte; |
|
320 if( mmapFree(pPrior)==0 ){ |
|
321 p = pPrior; |
|
322 p--; |
|
323 nByte = (int)*p; |
|
324 mem.nowUsed -= nByte; |
|
325 free(p); |
|
326 if( mem.nowUsed==0 ){ |
|
327 mmapUnmap(); |
|
328 } |
|
329 } |
|
330 } |
|
331 |
|
332 /* |
|
333 ** Allocate nBytes of memory |
|
334 */ |
|
335 void *sqlite3_malloc(int nBytes){ |
|
336 sqlite3_int64 *p = 0; |
|
337 if( nBytes>0 ){ |
|
338 memsys4Enter(); |
|
339 p = memsys4Malloc(nBytes); |
|
340 sqlite3_mutex_leave(mem.mutex); |
|
341 } |
|
342 return (void*)p; |
|
343 } |
|
344 |
|
345 /* |
|
346 ** Free memory. |
|
347 */ |
|
348 void sqlite3_free(void *pPrior){ |
|
349 if( pPrior==0 ){ |
|
350 return; |
|
351 } |
|
352 assert( mem.mutex!=0 ); |
|
353 sqlite3_mutex_enter(mem.mutex); |
|
354 memsys4Free(pPrior); |
|
355 sqlite3_mutex_leave(mem.mutex); |
|
356 } |
|
357 |
|
358 |
|
359 |
|
360 /* |
|
361 ** Change the size of an existing memory allocation |
|
362 */ |
|
363 void *sqlite3_realloc(void *pPrior, int nBytes){ |
|
364 int nOld; |
|
365 sqlite3_int64 *p; |
|
366 if( pPrior==0 ){ |
|
367 return sqlite3_malloc(nBytes); |
|
368 } |
|
369 if( nBytes<=0 ){ |
|
370 sqlite3_free(pPrior); |
|
371 return 0; |
|
372 } |
|
373 nOld = memsys4Size(pPrior); |
|
374 if( nBytes<=nOld && nBytes>=nOld-128 ){ |
|
375 return pPrior; |
|
376 } |
|
377 assert( mem.mutex!=0 ); |
|
378 sqlite3_mutex_enter(mem.mutex); |
|
379 p = memsys4Malloc(nBytes); |
|
380 if( p ){ |
|
381 if( nOld<nBytes ){ |
|
382 memcpy(p, pPrior, nOld); |
|
383 }else{ |
|
384 memcpy(p, pPrior, nBytes); |
|
385 } |
|
386 memsys4Free(pPrior); |
|
387 } |
|
388 assert( mem.mutex!=0 ); |
|
389 sqlite3_mutex_leave(mem.mutex); |
|
390 return (void*)p; |
|
391 } |
|
392 |
|
393 #endif /* SQLITE_MMAP_HEAP_SIZE */ |