2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006 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: Daniel Cowgill <dcowgill@communityconnect.com> |
16 | Rasmus Lerdorf <rasmus@php.net> |
17 +----------------------------------------------------------------------+
19 This software was contributed to PHP by Community Connect Inc. in 2002
20 and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
21 Future revisions and derivatives of this source code must acknowledge
22 Community Connect Inc. as the original contributor of this module by
23 leaving this note intact in the source code.
25 All other licensing and usage conditions are those of the PHP Group.
29 /* $Id: apc_sma.c,v 1.69 2007/12/26 21:35:39 gopalv Exp $ */
33 #include "apc_globals.h"
38 void *apc_mmap(char *file_mask, size_t size);
39 void apc_unmap(void* shmaddr, size_t size);
42 /* {{{ locking macros */
43 #define LOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_lock(c); }
44 #define RDLOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_rdlock(c); }
45 #define UNLOCK(c) { apc_lck_unlock(c); HANDLE_UNBLOCK_INTERRUPTIONS(); }
48 enum { DEFAULT_NUMSEG=1, DEFAULT_SEGSIZE=30*1024*1024 };
50 static int sma_initialized = 0; /* true if the sma has been initialized */
51 static unsigned int sma_numseg; /* number of shm segments to allow */
52 static size_t sma_segsize; /* size of each shm segment */
53 static size_t* sma_segments; /* array of shm segment ids */
54 static void** sma_shmaddrs; /* array of shm segment addresses */
55 static int sma_lastseg = 0; /* index of MRU segment */
57 typedef struct header_t header_t;
59 apc_lck_t sma_lock; /* segment lock, MUST BE ALIGNED for futex locks */
60 size_t segsize; /* size of entire segment */
61 size_t avail; /* bytes available (not necessarily contiguous) */
62 size_t nfoffset; /* start next fit search from this offset */
63 #if ALLOC_DISTRIBUTION
69 /* do not enable for threaded http servers */
70 /* #define __APC_SMA_DEBUG__ 1 */
72 #ifdef __APC_SMA_DEBUG__
73 /* global counter for identifying blocks
74 * Technically it is possible to do the same
75 * using offsets, but double allocations of the
76 * same offset can happen. */
77 static volatile size_t block_id = 0;
80 #define APC_SMA_CANARIES 1
82 typedef struct block_t block_t;
84 size_t size; /* size of this block */
85 size_t next; /* offset in segment of next free block */
86 #ifdef APC_SMA_CANARIES
87 size_t canary; /* canary to check for memory overwrites */
89 #ifdef __APC_SMA_DEBUG__
90 size_t id; /* identifier for the memory block */
94 /* The macros BLOCKAT and OFFSET are used for convenience throughout this
95 * module. Both assume the presence of a variable shmaddr that points to the
96 * beginning of the shared memory segment in question. */
98 #define BLOCKAT(offset) ((block_t*)((char *)shmaddr + offset))
99 #define OFFSET(block) ((size_t)(((char*)block) - (char*)shmaddr))
101 /* Canary macros for setting, checking and resetting memory canaries */
102 #ifdef APC_SMA_CANARIES
103 #define SET_CANARY(v) (v)->canary = 0x42424242
104 #define CHECK_CANARY(v) assert((v)->canary == 0x42424242)
105 #define RESET_CANARY(v) (v)->canary = -42
107 #define SET_CANARY(v)
108 #define CHECK_CANARY(v)
109 #define RESET_CANARY(v)
116 #define max(a, b) ((a) > (b) ? (a) : (b))
118 /* {{{ ALIGNWORD: pad up x, aligned to the system's word boundary */
119 typedef union { void* p; int i; long l; double d; void (*f)(); } apc_word_t;
120 #define ALIGNWORD(x) (sizeof(apc_word_t) * (1 + (((x)-1)/sizeof(apc_word_t))))
121 #define MINBLOCKSIZE (ALIGNWORD(1) + ALIGNWORD(sizeof(block_t)))
124 /* {{{ sma_allocate: tries to allocate size bytes in a segment */
125 static int sma_allocate(void* shmaddr, size_t size)
127 header_t* header; /* header of shared memory segment */
128 block_t* prv; /* block prior to working block */
129 block_t* cur; /* working block in list */
130 block_t* prvnextfit; /* block before next fit */
131 size_t realsize; /* actual size of block needed, including header */
132 size_t last_offset; /* save the last search offset */
134 const size_t block_size = ALIGNWORD(sizeof(struct block_t));
136 realsize = ALIGNWORD(size + block_size);
139 * First, insure that the segment contains at least realsize free bytes,
140 * even if they are not contiguous.
142 header = (header_t*) shmaddr;
143 if (header->avail < realsize) {
147 prvnextfit = 0; /* initially null (no fit) */
150 /* If we have a next fit offset, start searching from there */
151 if(header->nfoffset) {
152 prv = BLOCKAT(header->nfoffset);
153 /* if prv is the last block, jump to the beginning */
155 prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
159 prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
164 while (prv->next != header->nfoffset) {
165 cur = BLOCKAT(prv->next);
166 #ifdef __APC_SMA_DEBUG__
169 /* If it can fit realiszie bytes in cur block, stop searching */
170 if (cur->size >= realsize) {
174 last_offset = prv->next;
176 if(wrapped && (prv->next >= header->nfoffset)) break;
178 /* Check to see if we need to wrap around and search from the top */
179 if(header->nfoffset && prv->next == 0) {
180 prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
181 #ifdef __APC_SMA_DEBUG__
189 if (prvnextfit == 0) {
190 header->nfoffset = 0;
195 cur = BLOCKAT(prv->next);
200 if (cur->size == realsize || (cur->size > realsize && cur->size < (realsize + (MINBLOCKSIZE * 2)))) {
201 /* cur is big enough for realsize, but too small to split - unlink it */
202 prv->next = cur->next;
205 block_t* nxt; /* the new block (chopped part of cur) */
206 size_t nxtoffset; /* offset of the block currently after cur */
207 size_t oldsize; /* size of cur before split */
209 /* nextfit is too big; split it into two smaller blocks */
210 nxtoffset = cur->next;
212 prv->next += realsize; /* skip over newly allocated block */
213 cur->size = realsize; /* Set the size of this new block */
214 nxt = BLOCKAT(prv->next);
215 nxt->next = nxtoffset; /* Re-link the shortened block */
216 nxt->size = oldsize - realsize; /* and fix the size */
218 #ifdef __APC_SMA_DEBUG__
223 /* update the block header */
224 header->avail -= cur->size;
225 #if ALLOC_DISTRIBUTION
226 header->adist[(int)(log(size)/log(2))]++;
229 header->nfoffset = last_offset;
232 #ifdef __APC_SMA_DEBUG__
233 cur->id = ++block_id;
234 fprintf(stderr, "allocate(realsize=%d,size=%d,id=%d)\n", (int)(size), (int)(cur->size), cur->id);
237 return OFFSET(cur) + block_size;
241 /* {{{ sma_deallocate: deallocates the block at the given offset */
242 static int sma_deallocate(void* shmaddr, int offset)
244 header_t* header; /* header of shared memory segment */
245 block_t* cur; /* the new block to insert */
246 block_t* prv; /* the block before cur */
247 block_t* nxt; /* the block after cur */
248 size_t size; /* size of deallocated block */
250 offset -= ALIGNWORD(sizeof(struct block_t));
253 /* find position of new block in free list */
254 cur = BLOCKAT(offset);
255 prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
259 #ifdef __APC_SMA_DEBUG__
261 fprintf(stderr, "free(%p, size=%d,id=%d)\n", cur, (int)(cur->size), cur->id);
263 while (prv->next != 0 && prv->next < offset) {
264 prv = BLOCKAT(prv->next);
265 #ifdef __APC_SMA_DEBUG__
272 /* insert new block after prv */
273 cur->next = prv->next;
276 #ifdef __APC_SMA_DEBUG__
281 /* update the block header */
282 header = (header_t*) shmaddr;
283 header->avail += cur->size;
286 if (((char *)prv) + prv->size == (char *) cur) {
287 /* cur and prv share an edge, combine them */
288 prv->size += cur->size;
289 prv->next = cur->next;
294 nxt = BLOCKAT(cur->next);
296 if (((char *)cur) + cur->size == (char *) nxt) {
297 /* cur and nxt shared an edge, combine them */
298 cur->size += nxt->size;
299 cur->next = nxt->next;
300 #ifdef __APC_SMA_DEBUG__
302 nxt->id = -1; /* assert this or set it ? */
306 header->nfoffset = 0; /* Reset the next fit search marker */
312 /* {{{ apc_sma_init */
314 void apc_sma_init(int numseg, size_t segsize, char *mmap_file_mask)
318 if (sma_initialized) {
325 * I don't think multiple anonymous mmaps makes any sense
326 * so force sma_numseg to 1 in this case
328 if(!mmap_file_mask ||
329 (mmap_file_mask && !strlen(mmap_file_mask)) ||
330 (mmap_file_mask && !strcmp(mmap_file_mask, "/dev/zero"))) {
333 sma_numseg = numseg > 0 ? numseg : DEFAULT_NUMSEG;
336 sma_numseg = numseg > 0 ? numseg : DEFAULT_NUMSEG;
339 sma_segsize = segsize > 0 ? segsize : DEFAULT_SEGSIZE;
341 sma_segments = (size_t*) apc_emalloc(sma_numseg*sizeof(size_t));
342 sma_shmaddrs = (void**) apc_emalloc(sma_numseg*sizeof(void*));
344 for (i = 0; i < sma_numseg; i++) {
350 sma_segments[i] = sma_segsize;
351 sma_shmaddrs[i] = apc_mmap(mmap_file_mask, sma_segsize);
352 if(sma_numseg != 1) memcpy(&mmap_file_mask[strlen(mmap_file_mask)-6], "XXXXXX", 6);
354 sma_segments[i] = apc_shm_create(NULL, i, sma_segsize);
355 sma_shmaddrs[i] = apc_shm_attach(sma_segments[i]);
357 shmaddr = sma_shmaddrs[i];
359 header = (header_t*) shmaddr;
360 apc_lck_create(NULL, 0, 1, header->sma_lock);
361 header->segsize = sma_segsize;
362 header->avail = sma_segsize - ALIGNWORD(sizeof(header_t)) - ALIGNWORD(sizeof(block_t));
363 header->nfoffset = 0;
364 #if ALLOC_DISTRIBUTION
367 for(j=0; j<30; j++) header->adist[j] = 0;
370 block = BLOCKAT(ALIGNWORD(sizeof(header_t)));
372 block->next = ALIGNWORD(sizeof(header_t)) + ALIGNWORD(sizeof(block_t));
374 #ifdef __APC_SMA_DEBUG__
377 block = BLOCKAT(block->next);
378 block->size = header->avail;
381 #ifdef __APC_SMA_DEBUG__
388 /* {{{ apc_sma_cleanup */
389 void apc_sma_cleanup()
393 assert(sma_initialized);
395 for (i = 0; i < sma_numseg; i++) {
396 apc_lck_destroy(((header_t*)sma_shmaddrs[i])->sma_lock);
398 apc_unmap(sma_shmaddrs[i], sma_segments[i]);
400 apc_shm_detach(sma_shmaddrs[i]);
404 apc_efree(sma_segments);
405 apc_efree(sma_shmaddrs);
409 /* {{{ apc_sma_malloc */
410 void* apc_sma_malloc(size_t n)
416 assert(sma_initialized);
417 LOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock);
419 off = sma_allocate(sma_shmaddrs[sma_lastseg], n);
421 void* p = (void *)(((char *)(sma_shmaddrs[sma_lastseg])) + off);
422 if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) += n; }
423 UNLOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock);
426 UNLOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock);
428 for (i = 0; i < sma_numseg; i++) {
429 if (i == sma_lastseg) {
432 LOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
433 off = sma_allocate(sma_shmaddrs[i], n);
435 void* p = (void *)(((char *)(sma_shmaddrs[i])) + off);
436 if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) += n; }
437 UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
441 UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
448 /* {{{ apc_sma_realloc */
449 void* apc_sma_realloc(void *p, size_t n)
452 return apc_sma_malloc(n);
456 /* {{{ apc_sma_strdup */
457 char* apc_sma_strdup(const char* s)
465 q = apc_sma_malloc(len);
472 /* {{{ apc_sma_free */
473 void apc_sma_free(void* p)
484 assert(sma_initialized);
486 for (i = 0; i < sma_numseg; i++) {
487 LOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
488 offset = (size_t)((char *)p - (char *)(sma_shmaddrs[i]));
489 if (p >= sma_shmaddrs[i] && offset < sma_segsize) {
490 d_size = sma_deallocate(sma_shmaddrs[i], offset);
491 if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) -= d_size; }
492 UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
495 UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
498 apc_eprint("apc_sma_free: could not locate address %p", p);
502 /* {{{ apc_sma_info */
503 apc_sma_info_t* apc_sma_info(zend_bool limited)
505 apc_sma_info_t* info;
506 apc_sma_link_t** link;
511 if (!sma_initialized) {
515 info = (apc_sma_info_t*) apc_emalloc(sizeof(apc_sma_info_t));
516 info->num_seg = sma_numseg;
517 info->seg_size = sma_segsize - ALIGNWORD(sizeof(header_t)) - ALIGNWORD(sizeof(block_t));
519 info->list = apc_emalloc(info->num_seg * sizeof(apc_sma_link_t*));
520 for (i = 0; i < sma_numseg; i++) {
521 info->list[i] = NULL;
524 if(limited) return info;
526 /* For each segment */
527 for (i = 0; i < sma_numseg; i++) {
528 RDLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
529 shmaddr = sma_shmaddrs[i];
530 prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
532 link = &info->list[i];
534 /* For each block in this segment */
535 while (prv->next != 0) {
536 block_t* cur = BLOCKAT(prv->next);
537 #ifdef __APC_SMA_DEBUG__
541 *link = apc_emalloc(sizeof(apc_sma_link_t));
542 (*link)->size = cur->size;
543 (*link)->offset = prv->next;
544 (*link)->next = NULL;
545 link = &(*link)->next;
549 UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
556 /* {{{ apc_sma_free_info */
557 void apc_sma_free_info(apc_sma_info_t* info)
561 for (i = 0; i < info->num_seg; i++) {
562 apc_sma_link_t* p = info->list[i];
564 apc_sma_link_t* q = p;
569 apc_efree(info->list);
574 /* {{{ apc_sma_get_avail_mem */
575 size_t apc_sma_get_avail_mem()
577 size_t avail_mem = 0;
580 for (i = 0; i < sma_numseg; i++) {
581 header_t* header = (header_t*) sma_shmaddrs[i];
582 avail_mem += header->avail;
588 #if ALLOC_DISTRIBUTION
589 size_t *apc_sma_get_alloc_distribution(void) {
590 header_t* header = (header_t*) sma_shmaddrs[0];
591 return header->adist;
596 /* {{{ apc_sma_check_integrity */
597 void apc_sma_check_integrity()
601 /* For each segment */
602 for (i = 0; i < sma_numseg; i++) {
603 char* shmaddr = sma_shmaddrs[i];
604 header_t* header = (header_t*) shmaddr;
605 block_t* prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
608 /* For each block in this segment */
609 while (prv->next != 0) {
610 block_t* cur = BLOCKAT(prv->next);
615 assert(avail == header->avail);
626 * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
627 * vim<600: expandtab sw=4 ts=4 sts=4