/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include"primpl.h"
/* ** We override malloc etc. on any platform which has preemption + ** nspr20 user level threads. When we're debugging, we can make our ** version of malloc fail occasionally.
*/ #ifdef _PR_OVERRIDE_MALLOC
/* ** Thread safe version of malloc, calloc, realloc, free
*/ # include <stdarg.h>
/* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- *
*/
/* * Defining SANITY will enable some checks which will tell you if the users * program did botch something
*/
/* * Defining EXTRA_SANITY will enable some checks which are mostly related * to internal conditions in malloc.c
*/
/* * Very verbose progress on stdout...
*/ # if 0 # define TRACE(foo) printf foo staticint malloc_event; # else # define TRACE(foo) # endif
/* XXX Pick a number, any number */ # define malloc_pagesize 4096UL # define malloc_pageshift 12UL
# ifdef XP_UNIX # include <stdio.h> # include <stdlib.h> # include <unistd.h> # include <string.h> # include <errno.h> # include <sys/types.h> # include <sys/mman.h> # endif
/* * This structure describes a page's worth of chunks.
*/
struct pginfo { struct pginfo* next; /* next on the free list */ char* page; /* Pointer to the page */
u_short size; /* size of this page's chunks */
u_short shift; /* How far to shift for this size chunks */
u_short free; /* How many free chunks */
u_short total; /* How many chunk */
u_long bits[1]; /* Which chunks are free */
};
struct pgfree { struct pgfree* next; /* next run of free pages */ struct pgfree* prev; /* prev run of free pages */ char* page; /* pointer to free pages */ char* end; /* pointer to end of free pages */
u_long size; /* number of bytes free */
};
/* * How many bits per u_long in the bitmap. * Change only if not 8 bits/byte
*/ # define MALLOC_BITS (8 * sizeof(u_long))
/* * Magic values to put in the page_directory
*/ # define MALLOC_NOT_MINE ((struct pginfo*)0) # define MALLOC_FREE ((struct pginfo*)1) # define MALLOC_FIRST ((struct pginfo*)2) # define MALLOC_FOLLOW ((struct pginfo*)3) # define MALLOC_MAGIC ((struct pginfo*)4)
/* * Set to one when malloc_init has been called
*/ staticunsigned initialized;
/* * The size of a page. * Must be a integral multiplum of the granularity of mmap(2). * Your toes will curl if it isn't a power of two
*/ # define malloc_pagemask ((malloc_pagesize) - 1)
/* * The size of the largest chunk. * Half a page.
*/ # define malloc_maxsize ((malloc_pagesize) >> 1)
/* * The smallest allocation we bother about. * Must be power of two
*/ # ifndef malloc_minsize staticunsigned malloc_minsize; # endif /* malloc_minsize */
/* * The largest chunk we care about. * Must be smaller than pagesize * Must be power of two
*/ # ifndef malloc_maxsize staticunsigned malloc_maxsize; # endif /* malloc_maxsize */
/* Make it this many pages */
i = index * sizeof *page_dir;
i /= malloc_pagesize;
i += 2;
/* Get new pages, if you used this much mem you don't care :-) */
young = (struct pginfo**)map_pages(i, 0); if (!young) { return 0;
}
/* Copy the old stuff */
memset(young, 0, i * malloc_pagesize);
memcpy(young, page_dir, malloc_ninfo * sizeof *page_dir);
/* register the new size */
malloc_ninfo = i * malloc_pagesize / sizeof *page_dir;
/* swap the pointers */
old = page_dir;
page_dir = young;
/* Mark the pages */
index = ((u_long)young >> malloc_pageshift) - malloc_origo;
page_dir[index] = MALLOC_FIRST; while (--i) {
page_dir[++index] = MALLOC_FOLLOW;
}
/* Now free the old stuff */
_PR_UnlockedFree(old); return 1;
}
/* * Set entry in page directory. * Extend page directory if need be.
*/ staticint set_pgdir(void* ptr, struct pginfo* info) {
u_long index = ((u_long)ptr >> malloc_pageshift) - malloc_origo;
# ifndef malloc_pageshift /* determine how much we shift by to get there */ for (i = malloc_pagesize; i > 1; i >>= 1) {
malloc_pageshift++;
} # endif /* malloc_pageshift */
# ifndef malloc_minsize /* * find the smallest size allocation we will bother about. * this is determined as the smallest allocation that can hold * it's own pginfo;
*/
i = 2; for (;;) { int j;
/* Figure out the size of the bits */
j = malloc_pagesize / i;
j /= 8; if (j < sizeof(u_long)) {
j = sizeof(u_long);
} if (sizeof(struct pginfo) + j - sizeof(u_long) <= i) { break;
}
i += i;
}
malloc_minsize = i; # endif /* malloc_minsize */
/* Allocate one page for the page directory */
page_dir = (struct pginfo**)map_pages(1, 0); # ifdef SANITY if (!page_dir) {
wrterror("fatal: my first mmap failed. (check limits ?)\n");
} # endif
/* * We need a maximum of malloc_pageshift buckets, steal these from the * front of the page_directory;
*/
malloc_origo = (u_long)page_dir >> malloc_pageshift;
malloc_origo -= malloc_pageshift;
/* Clear it */
memset(page_dir, 0, malloc_pagesize);
/* Find out how much it tells us */
malloc_ninfo = malloc_pagesize / sizeof *page_dir;
/* Plug the page directory into itself */
i = set_pgdir(page_dir, MALLOC_FIRST); # ifdef SANITY if (!i) {
wrterror("fatal: couldn't set myself in the page directory\n");
} # endif
/* Been here, done that */
initialized++;
}
/* * Allocate a number of complete pages
*/ staticvoid* malloc_pages(size_t size) { void *p, *delay_free = 0; int i; struct pgfree* pf;
u_long index;
/* How many pages ? */
size += (malloc_pagesize - 1);
size &= ~malloc_pagemask;
p = 0; /* Look for free pages before asking for more */ for (pf = free_list.next; pf; pf = pf->next) { # ifdef EXTRA_SANITY if (pf->page == pf->end) {
wrterror("zero entry on free_list\n");
} if (pf->page > pf->end) {
TRACE(("%6d !s %p %p %p <%d>\n", malloc_event++, pf, pf->page, pf->end,
__LINE__));
wrterror("sick entry on free_list\n");
} if ((void*)pf->page >= (void*)sbrk(0)) {
wrterror("entry on free_list past brk\n");
} if (page_dir[((u_long)pf->page >> malloc_pageshift) - malloc_origo] !=
MALLOC_FREE) {
TRACE(("%6d !f %p %p %p <%d>\n", malloc_event++, pf, pf->page, pf->end,
__LINE__));
wrterror("non-free first page on free-list\n");
} if (page_dir[((u_long)pf->end >> malloc_pageshift) - 1 - malloc_origo] !=
MALLOC_FREE) {
wrterror("non-free last page on free-list\n");
} # endif /* EXTRA_SANITY */ if (pf->size < size) { continue;
} elseif (pf->size == size) {
p = pf->page; if (pf->next) {
pf->next->prev = pf->prev;
}
pf->prev->next = pf->next;
delay_free = pf; break;
} else {
p = pf->page;
pf->page += size;
pf->size -= size; break;
}
} # ifdef EXTRA_SANITY if (p &&
page_dir[((u_long)p >> malloc_pageshift) - malloc_origo] != MALLOC_FREE) {
wrterror("allocated non-free page on free-list\n");
} # endif /* EXTRA_SANITY */
size >>= malloc_pageshift;
/* Map new pages */ if (!p) {
p = map_pages(size, 1);
}
if (p) { /* Mark the pages in the directory */
index = ((u_long)p >> malloc_pageshift) - malloc_origo;
page_dir[index] = MALLOC_FIRST; for (i = 1; i < size; i++) {
page_dir[index + i] = MALLOC_FOLLOW;
}
} if (delay_free) { if (!px) {
px = (struct pgfree*)delay_free;
} else {
_PR_UnlockedFree(delay_free);
}
} return p;
}
/* * Allocate a page of fragments
*/
staticint malloc_make_chunks(int bits) { struct pginfo* bp; void* pp; int i, k, l;
/* Allocate a new bucket */
pp = malloc_pages(malloc_pagesize); if (!pp) { return 0;
}
l = sizeof *bp - sizeof(u_long);
l += sizeof(u_long) *
(((malloc_pagesize >> bits) + MALLOC_BITS - 1) / MALLOC_BITS); if ((1 << (bits)) <= l + l) {
bp = (struct pginfo*)pp;
} else {
bp = (struct pginfo*)_PR_UnlockedMalloc(l);
} if (!bp) { return 0;
}
bp->size = (1 << bits);
bp->shift = bits;
bp->total = bp->free = malloc_pagesize >> bits;
bp->next = page_dir[bits];
bp->page = (char*)pp;
i = set_pgdir(pp, bp); if (!i) { return 0;
}
/* We can safely assume that there is nobody in this chain */
page_dir[bits] = bp;
/* set all valid bits in the bits */
k = bp->total;
i = 0; /* for(;k-i >= MALLOC_BITS; i += MALLOC_BITS) bp->bits[i / MALLOC_BITS] = ~0;
*/ for (; i < k; i++) {
set_bit(bp, i);
}
if (bp != pp) { return 1;
}
/* We may have used the first ones already */ for (i = 0; l > 0; i++) {
clr_bit(bp, i);
bp->free--;
bp->total--;
l -= (1 << bits);
} return 1;
}
/* * Allocate a fragment
*/ staticvoid* malloc_bytes(size_t size) {
size_t s; int j; struct pginfo* bp; int k;
u_long *lp, bf;
/* Don't bother with anything less than this */ if (size < malloc_minsize) {
size = malloc_minsize;
}
/* Find the right bucket */
j = 1;
s = size - 1; while (s >>= 1) {
j++;
}
/* If it's empty, make a page more of that size chunks */ if (!page_dir[j] && !malloc_make_chunks(j)) { return 0;
}
/* Find first word of bitmap which isn't empty */
bp = page_dir[j]; for (lp = bp->bits; !*lp; lp++);
/* Find that bit */
bf = *lp;
k = 0; while ((bf & 1) == 0) {
bf >>= 1;
k++;
}
# ifdef SANITY if (suicide) {
PR_Abort();
} # endif
/* used as free() */
TRACE(("%6d R %p %d\n", malloc_event++, ptr, size)); if (ptr && !size) {
_PR_UnlockedFree(ptr); return _PR_UnlockedMalloc(1);
}
/* used as malloc() */ if (!ptr) {
p = _PR_UnlockedMalloc(size); return p;
}
/* Find the page directory entry for the page in question */
page = (u_long)ptr >> malloc_pageshift;
index = page - malloc_origo;
/* * check if memory was allocated by memalign
*/
tmp_index = index; while (page_dir[tmp_index] == MALLOC_FOLLOW) {
tmp_index--;
} if (tmp_index != index) { /* * memalign-allocated memory
*/
index = tmp_index;
page = index + malloc_origo;
ptr = (void*)(page << malloc_pageshift);
}
TRACE(("%6d R2 %p %d\n", malloc_event++, ptr, size));
/* make sure it makes sense in some fashion */ if (index < malloc_pageshift || index > last_index) { # ifdef SANITY
wrtwarning("junk pointer passed to realloc()\n"); # endif return 0;
}
/* find the size of that allocation, and see if we need to relocate */
mp = &page_dir[index]; if (*mp == MALLOC_FIRST) {
osize = malloc_pagesize; while (mp[1] == MALLOC_FOLLOW) {
osize += malloc_pagesize;
mp++;
} if (!malloc_realloc && size < osize && size > malloc_maxsize &&
size > (osize - malloc_pagesize)) { return ptr;
}
} elseif (*mp >= MALLOC_MAGIC) {
osize = (*mp)->size; if (!malloc_realloc && size < osize &&
(size > (*mp)->size / 2 || (*mp)->size == malloc_minsize)) { return ptr;
}
} else { # ifdef SANITY
wrterror("realloc() of wrong page.\n"); # endif
}
/* try to reallocate */
p = _PR_UnlockedMalloc(size);
if (p) { /* copy the lesser of the two sizes */ if (osize < size) {
memcpy(p, ptr, osize);
} else {
memcpy(p, ptr, size);
}
_PR_UnlockedFree(ptr);
} # ifdef DEBUG elseif (malloc_abort) {
wrterror("realloc() returns NULL\n");
} # endif
TRACE(("%6d FP %p %d\n", malloc_event++, ptr, page)); /* Is it free already ? */ if (info == MALLOC_FREE) { # ifdef SANITY
wrtwarning("freeing free page at %p.\n", ptr); # endif return;
}
# ifdef SANITY /* Is it not the right place to begin ? */ if (info != MALLOC_FIRST) {
wrterror("freeing wrong page.\n");
}
/* Is this really a pointer to a page ? */ if ((u_long)ptr & malloc_pagemask) {
wrterror("freeing messed up page pointer.\n");
} # endif
/* Count how many pages it is anyway */
page_dir[index] = MALLOC_FREE; for (i = 1; page_dir[index + i] == MALLOC_FOLLOW; i++) {
page_dir[index + i] = MALLOC_FREE;
}
/* * Free a chunk, and possibly the page it's on, if the page becomes empty.
*/
staticvoid free_bytes(void* ptr, u_long page, int index, struct pginfo* info) { int i; struct pginfo** mp; void* vp;
/* Make sure that pointer is multiplum of chunk-size */ # ifdef SANITY if ((u_long)ptr & (info->size - 1)) {
wrterror("freeing messed up chunk pointer\n");
} # endif
/* Find the chunk number on the page */
i = ((u_long)ptr & malloc_pagemask) >> info->shift;
/* See if it's free already */ if (tst_bit(info, i)) { # ifdef SANITY
wrtwarning("freeing free chunk at %p\n", ptr); # endif return;
}
/* Mark it free */
set_bit(info, i);
info->free++;
/* If the page was full before, we need to put it on the queue now */ if (info->free == 1) {
mp = page_dir + info->shift; while (*mp && (*mp)->next && (*mp)->next->page < info->page) {
mp = &(*mp)->next;
}
info->next = *mp;
*mp = info; return;
}
/* If this page isn't empty, don't do anything. */ if (info->free != info->total) { return;
}
/* We may want to keep at least one page of each size chunks around. */
mp = page_dir + info->shift; if (0 && (*mp == info) && !info->next) { return;
}
/* Find & remove this page in the queue */ while (*mp != info) {
mp = &((*mp)->next); # ifdef EXTRA_SANITY if (!*mp) {
TRACE(("%6d !q %p\n", malloc_event++, info));
wrterror("Not on queue\n");
} # endif
}
*mp = info->next;
/* Free the page & the info structure if need be */
set_pgdir(info->page, MALLOC_FIRST); if ((void*)info->page == (void*)info) {
_PR_UnlockedFree(info->page);
} else {
vp = info->page;
_PR_UnlockedFree(info);
_PR_UnlockedFree(vp);
}
}
TRACE(("%6d F %p\n", malloc_event++, ptr)); /* This is legal */ if (!ptr) { return;
}
# ifdef SANITY /* There wouldn't be anything to free */ if (!initialized) {
wrtwarning("free() called before malloc() ever got called\n"); return;
} # endif
# ifdef SANITY if (suicide) {
PR_Abort();
} # endif
/* Find the page directory entry for the page in question */
page = (u_long)ptr >> malloc_pageshift;
index = page - malloc_origo;
/* * check if memory was allocated by memalign
*/
tmp_index = index; while (page_dir[tmp_index] == MALLOC_FOLLOW) {
tmp_index--;
} if (tmp_index != index) { /* * memalign-allocated memory
*/
index = tmp_index;
page = index + malloc_origo;
ptr = (void*)(page << malloc_pageshift);
} /* make sure it makes sense in some fashion */ if (index < malloc_pageshift) { # ifdef SANITY
wrtwarning("junk pointer %p (low) passed to free()\n", ptr); # endif return;
} if (index > last_index) { # ifdef SANITY
wrtwarning("junk pointer %p (high) passed to free()\n", ptr); # endif return;
}
/* handle as page-allocation or chunk allocation */
info = page_dir[index]; if (info < MALLOC_MAGIC) {
free_pages((char*)ptr, page, index, info);
} else {
free_bytes(ptr, page, index, info);
} return;
} #endif/* _PR_OVERRIDE_MALLOC */
¤ Dauer der Verarbeitung: 0.63 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.