New PHP5 APC - version 3.0.19, using PHP5 5.2.0-8+etch11,
[php5-apc.git] / apc_pool.c
1 /*
2   +----------------------------------------------------------------------+
3   | APC                                                                  |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2008 The PHP Group                                     |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt.                                 |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Gopal Vijayaraghavan <gopalv@yahoo-inc.com>                 |
16   +----------------------------------------------------------------------+
17
18    This software was contributed to PHP by Yahoo! Inc. in 2008.
19    
20    Future revisions and derivatives of this source code must acknowledge
21    Yahoo! Inc. as the original contributor of this module by
22    leaving this note intact in the source code.
23
24    All other licensing and usage conditions are those of the PHP Group.
25
26  */
27
28 /* $Id: apc_pool.c,v 3.3 2008/01/09 12:30:39 gopalv Exp $ */
29
30
31 #include "apc_pool.h"
32 #include <assert.h>
33
34 #ifdef HAVE_VALGRIND_MEMCHECK_H
35 #include <valgrind/memcheck.h>
36 #endif
37
38 /* {{{ typedefs */
39 typedef struct _pool_block
40 {
41     size_t              avail;
42     size_t              capacity;
43     unsigned char       *mark;
44     struct _pool_block  *next;
45     unsigned             :0; /* this should align to word */
46     unsigned char       data[0]; 
47 }pool_block;
48
49 /*
50    parts in ? are optional and turned on for fun, memory loss,
51    and for something else that I forgot about ... ah, debugging
52
53                  |--------> data[0]         |<-- non word boundary (too)
54    +-------------+--------------+-----------+-------------+-------------->>>
55    | pool_block  | ?sizeinfo<1> | block<1>  | ?redzone<1> | ?sizeinfo<2> 
56    |             |  (size_t)    |           | padded left |    
57    +-------------+--------------+-----------+-------------+-------------->>>
58  */
59
60 struct _apc_pool
61 {
62     apc_malloc_t allocate;
63     apc_free_t   deallocate;
64
65     size_t     dsize;
66     void       *owner;
67
68     struct
69     {
70         unsigned int redzones:1;
71         unsigned int sizeinfo:1;
72     } options;
73
74     pool_block *head;
75 };
76 /* }}} */
77
78 /* {{{ redzone code */
79 static const unsigned char decaff[] =  {
80     0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad,
81     0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad,
82     0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad,
83     0xde, 0xca, 0xff, 0xc0, 0xff, 0xee, 0xba, 0xad
84 };
85
86 /* a redzone is at least 4 (0xde,0xca,0xc0,0xff) bytes */
87 #define REDZONE_SIZE(size) \
88     ((ALIGNWORD((size)) > ((size) + 4)) ? \
89         (ALIGNWORD((size)) - (size)) : /* does not change realsize */\
90         ALIGNWORD((size)) - (size) + ALIGNWORD((sizeof(char)))) /* adds 1 word to realsize */
91     
92 #define SIZEINFO_SIZE ALIGNWORD(sizeof(size_t))
93
94 #define MARK_REDZONE(block, redsize) do {\
95        memcpy(block, decaff, redsize );\
96     } while(0)
97
98 #define CHECK_REDZONE(block, redsize) (memcmp(block, decaff, redsize) == 0)
99
100 /* }}} */
101
102 #define APC_POOL_OPTION(pool, option) ((pool)->options.option)
103
104 /* {{{ create_pool_block */
105 static pool_block* create_pool_block(apc_pool *pool, size_t size)
106 {
107     size_t realsize = sizeof(pool_block) + ALIGNWORD(size);
108     
109     pool_block* entry = pool->allocate(realsize);
110
111     entry->avail = entry->capacity = size;
112
113     entry->mark = entry->data;
114
115     entry->next = pool->head;
116
117     pool->head = entry;
118
119     return entry;
120 }
121 /* }}} */
122
123 /* {{{ apc_pool_create */
124 apc_pool* apc_pool_create(apc_pool_type pool_type, 
125                             apc_malloc_t allocate, 
126                             apc_free_t deallocate)
127 {
128     apc_pool* pool = NULL;
129     size_t dsize = 0;
130
131     /* sanity checks */
132     assert(sizeof(decaff) > REDZONE_SIZE(ALIGNWORD(sizeof(char))));
133     assert(sizeof(pool_block) == ALIGNWORD(sizeof(pool_block)));
134
135     assert(APC_POOL_SIZE_MASK & (APC_POOL_SIZEINFO | APC_POOL_REDZONES) == 0);
136
137     switch(pool_type & APC_POOL_SIZE_MASK) {
138         case APC_SMALL_POOL:
139             dsize = 512;
140             break;
141
142         case APC_LARGE_POOL:
143             dsize = 8192;
144             break;
145
146         case APC_MEDIUM_POOL:
147             dsize = 4096;
148             break;
149
150         default:
151             return NULL;
152     }
153
154     pool = (apc_pool*)allocate(sizeof(apc_pool));
155
156     if(!pool) {
157         return NULL;
158     }
159
160     pool->allocate = allocate;
161     pool->deallocate = deallocate;
162     pool->dsize = dsize;
163     pool->head = NULL;
164
165     APC_POOL_OPTION(pool, redzones) = (pool_type & APC_POOL_REDZONES) != 0;
166     APC_POOL_OPTION(pool, sizeinfo) = (pool_type & APC_POOL_SIZEINFO) != 0;
167
168     if(!create_pool_block(pool, dsize)) {
169         deallocate(pool);
170         return NULL;
171     }
172
173     return pool; 
174 }
175 /* }}} */
176
177 /* {{{ apc_pool_destroy */
178 void apc_pool_destroy(apc_pool *pool)
179 {
180
181     apc_free_t deallocate = pool->deallocate;
182     pool_block *entry;
183     pool_block *tmp;
184
185     entry = pool->head;
186
187     while(entry != NULL) {
188         tmp = entry->next;
189         deallocate(entry);
190         entry = tmp;
191     }
192
193     deallocate(pool);
194 }
195 /* }}} */
196
197 /* {{{ apc_pool_alloc */
198 void* apc_pool_alloc(apc_pool *pool, size_t size)
199 {
200     unsigned char *p = NULL;
201     size_t realsize = ALIGNWORD(size);
202     size_t poolsize;
203     unsigned char *redzone  = NULL;
204     size_t redsize  = 0;
205     size_t *sizeinfo= NULL;
206
207     pool_block *entry;
208
209
210     if(APC_POOL_OPTION(pool, redzones)) {
211         redsize = REDZONE_SIZE(size); /* redsize might be re-using word size padding */
212         realsize = size + redsize;    /* recalculating realsize */
213     } else {
214         redsize = realsize - size; /* use padding space */
215     }
216
217     if(APC_POOL_OPTION(pool, sizeinfo)) {
218         realsize += ALIGNWORD(sizeof(size_t));
219     }
220
221
222     for(entry = pool->head; entry != NULL; entry = entry->next) {
223         if(entry->avail >= realsize) {
224             goto found;
225         }
226     }
227
228     poolsize = ALIGNSIZE(realsize, pool->dsize);
229
230     entry = create_pool_block(pool, poolsize);
231
232     if(!entry) {
233         return NULL;
234     }
235
236 found:
237     p = entry->mark;
238
239     if(APC_POOL_OPTION(pool, sizeinfo)) {
240         sizeinfo = (size_t*)p;
241         p += SIZEINFO_SIZE;
242         *sizeinfo = size;
243     }
244     
245     redzone = p + size;
246
247     if(APC_POOL_OPTION(pool, redzones)) {
248         MARK_REDZONE(redzone, redsize);
249     }
250
251 #ifdef VALGRIND_MAKE_MEM_NOACCESS
252     if(redsize != 0) {
253         VALGRIND_MAKE_MEM_NOACCESS(redzone, redsize);
254     }
255 #endif
256
257     entry->avail -= realsize;
258     entry->mark  += realsize;
259
260 #ifdef VALGRIND_MAKE_MEM_UNDEFINED
261     /* need to write before reading data off this */
262     VALGRIND_MAKE_MEM_UNDEFINED(p, size);
263 #endif
264
265     return (void*)p;
266 }
267 /* }}} */
268
269 /* {{{ apc_pool_free */
270 /*
271  * free does not do anything other than
272  * check for redzone values when free'ing
273  * data areas.
274  */
275 void apc_pool_free(apc_pool *pool, void *p)
276 {
277     if(!APC_POOL_OPTION(pool, sizeinfo) || 
278         !APC_POOL_OPTION(pool, redzones)) {
279     }
280 }
281 /* }}} */
282
283 /* {{{ apc_pool_check_integrity */
284 /*
285  * Checking integrity at runtime, does an
286  * overwrite check only when the sizeinfo
287  * is set.
288  */
289 int apc_pool_check_integrity(apc_pool *pool) 
290 {
291     pool_block *entry;
292     size_t *sizeinfo = NULL;
293     unsigned char *start;
294     size_t realsize;
295     unsigned char   *redzone;
296     size_t redsize;
297
298     for(entry = pool->head; entry != NULL; entry = entry->next) {
299         start = (unsigned char *)entry + ALIGNWORD(sizeof(pool_block));
300         if((entry->mark - start) != (entry->capacity - entry->avail)) {
301             return 0;
302         }
303     }
304
305     if(!APC_POOL_OPTION(pool, sizeinfo) || 
306         !APC_POOL_OPTION(pool, redzones)) {
307         return 1;
308     }
309
310     for(entry = pool->head; entry != NULL; entry = entry->next) {
311         start = (unsigned char *)entry + ALIGNWORD(sizeof(pool_block));
312         
313         while(start < entry->mark) {
314             sizeinfo = (size_t*)start;
315             /* redzone starts where real data ends, in a non-word boundary
316              * redsize is at least 4 bytes + whatever's needed to make it
317              * to another word boundary.
318              */
319             redzone = start + SIZEINFO_SIZE + (*sizeinfo);
320             redsize = REDZONE_SIZE(*sizeinfo);
321             if(!CHECK_REDZONE(redzone, redsize))
322             {
323                 /*
324                 fprintf(stderr, "Redzone check failed for %p\n", 
325                                 start + ALIGNWORD(sizeof(size_t)));*/
326                 return 0;
327             }
328             realsize = SIZEINFO_SIZE + *sizeinfo + redsize;
329             start += realsize;
330         }
331     }
332
333     return 1;
334 }
335 /* }}} */
336
337 /*
338  * Local variables:
339  * tab-width: 4
340  * c-basic-offset: 4
341  * End:
342  * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
343  * vim<600: expandtab sw=4 ts=4 sts=4
344  */