Import polipo--devel--0--base-0.
[polipo.git] / chunk.c
blobc3caa00d111974e4b00fee341043310f6eb83a91
1 /*
2 Copyright (c) 2003, 2004 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
23 #include "polipo.h"
25 #define MB (1024 * 1024)
26 int chunkLowMark = 0,
27 chunkCriticalMark = 0,
28 chunkHighMark = 8 * MB;
30 void
31 preinitChunks()
33 CONFIG_VARIABLE(chunkLowMark, CONFIG_INT,
34 "Low mark for chunk memory (0 = auto).");
35 CONFIG_VARIABLE(chunkCriticalMark, CONFIG_INT,
36 "Critical mark for chunk memory (0 = auto).");
37 CONFIG_VARIABLE(chunkHighMark, CONFIG_INT,
38 "High mark for chunk memory.");
41 static void
42 initChunksCommon()
44 #define ROUND_CHUNKS(a) a = (((a) + CHUNK_SIZE - 1) / CHUNK_SIZE) * CHUNK_SIZE;
45 int q;
47 if(CHUNK_SIZE != 1 << log2_ceil(CHUNK_SIZE)) {
48 do_log(L_ERROR, "CHUNK SIZE %d is not a power of two.\n", CHUNK_SIZE);
49 exit(1);
52 ROUND_CHUNKS(chunkHighMark);
53 ROUND_CHUNKS(chunkCriticalMark);
54 ROUND_CHUNKS(chunkLowMark);
56 if(chunkHighMark < 8 * CHUNK_SIZE) {
57 chunkHighMark = 8 * CHUNK_SIZE;
58 do_log(L_WARN, "Impossibly low chunkHighMark -- setting to %d.\n",
59 chunkHighMark);
62 q = 0;
63 if(chunkLowMark <= 0) q = 1;
64 if(chunkLowMark < 4 * CHUNK_SIZE ||
65 chunkLowMark > chunkHighMark - 4 * CHUNK_SIZE) {
66 chunkLowMark = MIN(chunkHighMark - 4 * CHUNK_SIZE,
67 chunkHighMark * 3 / 4);
68 ROUND_CHUNKS(chunkLowMark);
69 if(!q) do_log(L_WARN, "Inconsistent chunkLowMark -- setting to %d.\n",
70 chunkLowMark);
73 q = 0;
74 if(chunkCriticalMark <= 0) q = 1;
75 if(chunkCriticalMark >= chunkHighMark - 2 * CHUNK_SIZE ||
76 chunkCriticalMark <= chunkLowMark + 2 * CHUNK_SIZE) {
77 chunkCriticalMark =
78 MIN(chunkHighMark - 2 * CHUNK_SIZE,
79 chunkLowMark + (chunkHighMark - chunkLowMark) * 15 / 16);
80 ROUND_CHUNKS(chunkCriticalMark);
81 if(!q) do_log(L_WARN, "Inconsistent chunkCriticalMark -- "
82 "setting to %d.\n", chunkCriticalMark);
84 #undef ROUND_CHUNKS
88 int used_chunks = 0;
90 static void
91 maybe_free_chunks(int arenas, int force)
93 if(force || used_chunks >= CHUNKS(chunkHighMark)) {
94 discardObjects(force, force);
97 if(arenas)
98 free_chunk_arenas();
100 if(used_chunks >= CHUNKS(chunkLowMark) && !objectExpiryScheduled) {
101 TimeEventHandlerPtr event;
102 event = scheduleTimeEvent(1, discardObjectsHandler, 0, NULL);
103 if(event)
104 objectExpiryScheduled = 1;
110 #ifdef MALLOC_CHUNKS
112 void
113 initChunks(void)
115 do_log(L_WARN, "Warning: using malloc(3) for chunk allocation.\n");
116 used_chunks = 0;
117 initChunksCommon();
120 void
121 free_chunk_arenas()
123 return;
126 void *
127 get_chunk()
129 void *chunk;
131 if(used_chunks > CHUNKS(chunkHighMark))
132 maybe_free_chunks(0, 0);
133 if(used_chunks > CHUNKS(chunkHighMark))
134 return NULL;
135 chunk = malloc(CHUNK_SIZE);
136 if(!chunk) {
137 maybe_free_chunks(1, 1);
138 chunk = malloc(CHUNK_SIZE);
139 if(!chunk)
140 return NULL;
142 used_chunks++;
143 return chunk;
146 void *
147 maybe_get_chunk()
149 void *chunk;
150 if(used_chunks > CHUNKS(chunkHighMark))
151 return NULL;
152 chunk = malloc(CHUNK_SIZE);
153 if(chunk)
154 used_chunks++;
155 return chunk;
158 void
159 dispose_chunk(void *chunk)
161 assert(chunk != NULL);
162 free(chunk);
163 used_chunks--;
166 void
167 free_chunks()
169 return;
172 #else
174 #ifndef MAP_FAILED
175 #define MAP_FAILED ((void*)((long int)-1))
176 #endif
178 /* Memory is organised into a number of chunks of ARENA_CHUNKS chunks
179 each. Every arena is pointed at by a struct _ChunkArena. */
180 /* If currentArena is not NULL, it points at an arena with free space.
181 This gives better locality, but is mostly useful in order to have
182 very fast dipose/get sequences. */
184 /* If you change this, you'll need to provide a replacement for ffs()
185 below. */
186 typedef unsigned int ChunkBitmap;
187 #define ARENA_CHUNKS ((int)sizeof(ChunkBitmap) * 8)
189 static int pagesize;
190 typedef struct _ChunkArena {
191 ChunkBitmap bitmap;
192 char *chunks;
193 } ChunkArenaRec, *ChunkArenaPtr;
195 static ChunkArenaPtr chunkArenas, currentArena;
196 static int numArenas;
197 #define CHUNK_IN_ARENA(chunk, arena) \
198 ((arena)->chunks && \
199 (char*)(chunk) >= (arena)->chunks && \
200 (char*)(chunk)<(arena)->chunks + (ARENA_CHUNKS * CHUNK_SIZE))
202 #define CHUNK_ARENA_INDEX(chunk, arena) \
203 (((char*)(chunk) - (arena)->chunks) / CHUNK_SIZE)
205 void
206 initChunks(void)
208 int i;
209 used_chunks = 0;
210 initChunksCommon();
211 pagesize = getpagesize();
212 if((CHUNK_SIZE * ARENA_CHUNKS) % pagesize != 0) {
213 do_log(L_ERROR,
214 "The arena size %d (%d x %d) "
215 "is not a multiple of the page size %d.\n",
216 ARENA_CHUNKS * CHUNK_SIZE, ARENA_CHUNKS, CHUNK_SIZE, pagesize);
217 abort();
219 numArenas =
220 (CHUNKS(chunkHighMark) + (ARENA_CHUNKS - 1)) / ARENA_CHUNKS;
221 chunkArenas = malloc(numArenas * sizeof(ChunkArenaRec));
222 if(chunkArenas == NULL) {
223 do_log(L_ERROR, "Couldn't allocate chunk arenas.\n");
224 polipoExit();
226 for(i = 0; i < numArenas; i++) {
227 chunkArenas[i].bitmap = ~(ChunkBitmap)0;
228 chunkArenas[i].chunks = NULL;
230 currentArena = NULL;
233 static ChunkArenaPtr
234 findArena()
236 ChunkArenaPtr arena = NULL;
237 int i;
239 for(i = 0; i < numArenas; i++) {
240 arena = &(chunkArenas[i]);
241 if(arena->bitmap != 0)
242 break;
243 else
244 arena = NULL;
247 assert(arena != NULL);
249 if(!arena->chunks) {
250 void *p;
251 p = mmap(NULL, CHUNK_SIZE * ARENA_CHUNKS, PROT_READ | PROT_WRITE,
252 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
253 if(p == MAP_FAILED) {
254 do_log_error(L_ERROR, errno, "Couldn't allocate chunk");
255 maybe_free_chunks(1, 1);
256 return NULL;
258 arena->chunks = p;
260 return arena;
263 void *
264 get_chunk()
266 int i;
267 ChunkArenaPtr arena = NULL;
269 if(currentArena) {
270 arena = currentArena;
271 } else {
272 if(used_chunks >= CHUNKS(chunkHighMark))
273 maybe_free_chunks(0, 0);
275 if(used_chunks >= CHUNKS(chunkHighMark))
276 return NULL;
278 arena = findArena();
279 if(!arena)
280 return NULL;
282 i = ffs(arena->bitmap) - 1;
283 arena->bitmap &= ~(1 << i);
284 if(arena->bitmap != 0)
285 currentArena = arena;
286 else
287 currentArena = NULL;
288 used_chunks++;
289 return arena->chunks + CHUNK_SIZE * i;
292 void *
293 maybe_get_chunk()
295 int i;
296 ChunkArenaPtr arena = NULL;
298 if(currentArena) {
299 arena = currentArena;
300 } else {
301 if(used_chunks >= CHUNKS(chunkHighMark))
302 return NULL;
304 arena = findArena();
305 if(!arena)
306 return NULL;
308 i = ffs(arena->bitmap) - 1;
309 arena->bitmap &= ~(1 << i);
310 if(arena->bitmap != 0)
311 currentArena = arena;
312 else
313 currentArena = NULL;
314 used_chunks++;
315 return arena->chunks + CHUNK_SIZE * i;
318 void
319 dispose_chunk(void *chunk)
321 ChunkArenaPtr arena = NULL;
322 int i;
324 assert(chunk != NULL);
326 if(currentArena && CHUNK_IN_ARENA(chunk, currentArena)) {
327 arena = currentArena;
328 } else {
329 for(i = 0; i < numArenas; i++) {
330 arena = &(chunkArenas[i]);
331 if(CHUNK_IN_ARENA(chunk, arena))
332 break;
335 assert(arena && arena->chunks);
337 i = CHUNK_ARENA_INDEX(chunk, arena);
338 arena->bitmap |= (1 << i);
339 used_chunks--;
340 currentArena = arena;
343 void
344 free_chunk_arenas()
346 ChunkArenaPtr arena;
347 int i, rc;
349 for(i = 0; i < numArenas; i++) {
350 arena = &(chunkArenas[i]);
351 if(arena->bitmap == (ChunkBitmap)~0 && arena->chunks) {
352 rc = munmap(arena->chunks, CHUNK_SIZE * ARENA_CHUNKS);
353 if(rc < 0) {
354 do_log_error(L_ERROR, errno, "Couldn't unmap memory");
355 continue;
357 arena->chunks = NULL;
360 if(currentArena && currentArena->chunks == NULL)
361 currentArena = NULL;
363 #endif