Index: mozilla/nsprpub/pr/src/malloc/prmalloc.c |
=================================================================== |
--- mozilla/nsprpub/pr/src/malloc/prmalloc.c (revision 191424) |
+++ mozilla/nsprpub/pr/src/malloc/prmalloc.c (working copy) |
@@ -1,1142 +0,0 @@ |
-/* -*- 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> |
- |
-#ifdef DEBUG |
-#define SANITY |
-#define EXTRA_SANITY |
-#else |
-#undef SANITY |
-#undef EXTRA_SANITY |
-#endif |
- |
-/* Forward decls */ |
-void *_PR_UnlockedMalloc(size_t size); |
-void _PR_UnlockedFree(void *ptr); |
-void *_PR_UnlockedRealloc(void *ptr, size_t size); |
-void *_PR_UnlockedCalloc(size_t n, size_t elsize); |
- |
-/************************************************************************/ |
- |
-/* |
- * ---------------------------------------------------------------------------- |
- * "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 |
-static int 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 |
- */ |
-static unsigned 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) |
- |
-/* |
- * malloc_pagesize == 1 << malloc_pageshift |
- */ |
-#ifndef malloc_pageshift |
-static unsigned malloc_pageshift; |
-#endif /* malloc_pageshift */ |
- |
-/* |
- * The smallest allocation we bother about. |
- * Must be power of two |
- */ |
-#ifndef malloc_minsize |
-static unsigned malloc_minsize; |
-#endif /* malloc_minsize */ |
- |
-/* |
- * The largest chunk we care about. |
- * Must be smaller than pagesize |
- * Must be power of two |
- */ |
-#ifndef malloc_maxsize |
-static unsigned malloc_maxsize; |
-#endif /* malloc_maxsize */ |
- |
-#ifndef malloc_cache |
-static unsigned malloc_cache; |
-#endif /* malloc_cache */ |
- |
-/* |
- * The offset from pagenumber to index into the page directory |
- */ |
-static u_long malloc_origo; |
- |
-/* |
- * The last index in the page directory we care about |
- */ |
-static u_long last_index; |
- |
-/* |
- * Pointer to page directory. |
- * Allocated "as if with" malloc |
- */ |
-static struct pginfo **page_dir; |
- |
-/* |
- * How many slots in the page directory |
- */ |
-static unsigned malloc_ninfo; |
- |
-/* |
- * Free pages line up here |
- */ |
-static struct pgfree free_list; |
- |
-/* |
- * Abort() if we fail to get VM ? |
- */ |
-static int malloc_abort; |
- |
-#ifdef SANITY |
-/* |
- * Are we trying to die ? |
- */ |
-static int suicide; |
-#endif |
- |
-/* |
- * dump statistics |
- */ |
-static int malloc_stats; |
- |
-/* |
- * always realloc ? |
- */ |
-static int malloc_realloc; |
- |
-/* |
- * my last break. |
- */ |
-static void *malloc_brk; |
- |
-/* |
- * one location cache for free-list holders |
- */ |
-static struct pgfree *px; |
- |
-static int set_pgdir(void *ptr, struct pginfo *info); |
-static int extend_page_directory(u_long index); |
- |
-#ifdef SANITY |
-void |
-malloc_dump(FILE *fd) |
-{ |
- struct pginfo **pd; |
- struct pgfree *pf; |
- int j; |
- |
- pd = page_dir; |
- |
- /* print out all the pages */ |
- for(j=0;j<=last_index;j++) { |
- fprintf(fd,"%08lx %5d ",(j+malloc_origo) << malloc_pageshift,j); |
- if (pd[j] == MALLOC_NOT_MINE) { |
- for(j++;j<=last_index && pd[j] == MALLOC_NOT_MINE;j++) |
- ; |
- j--; |
- fprintf(fd,".. %5d not mine\n", j); |
- } else if (pd[j] == MALLOC_FREE) { |
- for(j++;j<=last_index && pd[j] == MALLOC_FREE;j++) |
- ; |
- j--; |
- fprintf(fd,".. %5d free\n", j); |
- } else if (pd[j] == MALLOC_FIRST) { |
- for(j++;j<=last_index && pd[j] == MALLOC_FOLLOW;j++) |
- ; |
- j--; |
- fprintf(fd,".. %5d in use\n", j); |
- } else if (pd[j] < MALLOC_MAGIC) { |
- fprintf(fd,"(%p)\n", pd[j]); |
- } else { |
- fprintf(fd,"%p %d (of %d) x %d @ %p --> %p\n", |
- pd[j],pd[j]->free, pd[j]->total, |
- pd[j]->size, pd[j]->page, pd[j]->next); |
- } |
- } |
- |
- for(pf=free_list.next; pf; pf=pf->next) { |
- fprintf(fd,"Free: @%p [%p...%p[ %ld ->%p <-%p\n", |
- pf,pf->page,pf->end,pf->size,pf->prev,pf->next); |
- if (pf == pf->next) { |
- fprintf(fd,"Free_list loops.\n"); |
- break; |
- } |
- } |
- |
- /* print out various info */ |
- fprintf(fd,"Minsize\t%d\n",malloc_minsize); |
- fprintf(fd,"Maxsize\t%ld\n",malloc_maxsize); |
- fprintf(fd,"Pagesize\t%ld\n",malloc_pagesize); |
- fprintf(fd,"Pageshift\t%ld\n",malloc_pageshift); |
- fprintf(fd,"FirstPage\t%ld\n",malloc_origo); |
- fprintf(fd,"LastPage\t%ld %lx\n",last_index+malloc_pageshift, |
- (last_index + malloc_pageshift) << malloc_pageshift); |
- fprintf(fd,"Break\t%ld\n",(u_long)sbrk(0) >> malloc_pageshift); |
-} |
- |
-static void wrterror(char *fmt, ...) |
-{ |
- char *q = "malloc() error: "; |
- char buf[100]; |
- va_list ap; |
- |
- suicide = 1; |
- |
- va_start(ap, fmt); |
- PR_vsnprintf(buf, sizeof(buf), fmt, ap); |
- va_end(ap); |
- fputs(q, stderr); |
- fputs(buf, stderr); |
- |
- malloc_dump(stderr); |
- PR_Abort(); |
-} |
- |
-static void wrtwarning(char *fmt, ...) |
-{ |
- char *q = "malloc() warning: "; |
- char buf[100]; |
- va_list ap; |
- |
- va_start(ap, fmt); |
- PR_vsnprintf(buf, sizeof(buf), fmt, ap); |
- va_end(ap); |
- fputs(q, stderr); |
- fputs(buf, stderr); |
-} |
-#endif /* SANITY */ |
- |
- |
-/* |
- * Allocate a number of pages from the OS |
- */ |
-static caddr_t |
-map_pages(int pages, int update) |
-{ |
- caddr_t result,tail; |
- |
- result = ((caddr_t)sbrk(0)) + malloc_pagemask - 1; |
- result = (caddr_t) ((u_long)result & ~malloc_pagemask); |
- tail = result + (pages << malloc_pageshift); |
- if (!brk(tail)) { |
- last_index = ((u_long)tail >> malloc_pageshift) - malloc_origo -1; |
- malloc_brk = tail; |
- TRACE(("%6d S %p .. %p\n",malloc_event++, result, tail)); |
- if (!update || last_index < malloc_ninfo || |
- extend_page_directory(last_index)) |
- return result; |
- } |
- TRACE(("%6d s %d %p %d\n",malloc_event++,pages,sbrk(0),errno)); |
-#ifdef EXTRA_SANITY |
- wrterror("map_pages fails\n"); |
-#endif |
- return 0; |
-} |
- |
-#define set_bit(_pi,_bit) \ |
- (_pi)->bits[(_bit)/MALLOC_BITS] |= 1L<<((_bit)%MALLOC_BITS) |
- |
-#define clr_bit(_pi,_bit) \ |
- (_pi)->bits[(_bit)/MALLOC_BITS] &= ~(1L<<((_bit)%MALLOC_BITS)); |
- |
-#define tst_bit(_pi,_bit) \ |
- ((_pi)->bits[(_bit)/MALLOC_BITS] & (1L<<((_bit)%MALLOC_BITS))) |
- |
-/* |
- * Extend page directory |
- */ |
-static int |
-extend_page_directory(u_long index) |
-{ |
- struct pginfo **young, **old; |
- int i; |
- |
- TRACE(("%6d E %lu\n",malloc_event++,index)); |
- |
- /* 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. |
- */ |
-static int |
-set_pgdir(void *ptr, struct pginfo *info) |
-{ |
- u_long index = ((u_long)ptr >> malloc_pageshift) - malloc_origo; |
- |
- if (index >= malloc_ninfo && !extend_page_directory(index)) |
- return 0; |
- page_dir[index] = info; |
- return 1; |
-} |
- |
-/* |
- * Initialize the world |
- */ |
-static void |
-malloc_init (void) |
-{ |
- int i; |
- char *p; |
- |
- TRACE(("%6d I\n",malloc_event++)); |
-#ifdef DEBUG |
- for (p=getenv("MALLOC_OPTIONS"); p && *p; p++) { |
- switch (*p) { |
- case 'a': malloc_abort = 0; break; |
- case 'A': malloc_abort = 1; break; |
- case 'd': malloc_stats = 0; break; |
- case 'D': malloc_stats = 1; break; |
- case 'r': malloc_realloc = 0; break; |
- case 'R': malloc_realloc = 1; break; |
- default: |
- wrtwarning("Unknown chars in MALLOC_OPTIONS\n"); |
- break; |
- } |
- } |
-#endif |
- |
-#ifndef malloc_pagesize |
- /* determine our pagesize */ |
- malloc_pagesize = getpagesize(); |
-#endif /* malloc_pagesize */ |
- |
-#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_cache |
- malloc_cache = 50 << malloc_pageshift; |
-#endif /* malloc_cache */ |
- |
-#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 |
- */ |
-static void *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; |
- else if (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 |
- */ |
- |
-static int |
-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 |
- */ |
-static void *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++; |
- } |
- |
- *lp ^= 1L<<k; /* clear it */ |
- bp->free--; |
- if (!bp->free) { |
- page_dir[j] = bp->next; |
- bp->next = 0; |
- } |
- k += (lp - bp->bits)*MALLOC_BITS; |
- return bp->page + (k << bp->shift); |
-} |
- |
-void *_PR_UnlockedMalloc(size_t size) |
-{ |
- void *result; |
- |
- /* Round up to a multiple of 8 bytes */ |
- if (size & 7) { |
- size = size + 8 - (size & 7); |
- } |
- |
- if (!initialized) |
- malloc_init(); |
- |
-#ifdef SANITY |
- if (suicide) |
- PR_Abort(); |
-#endif |
- |
- if (size <= malloc_maxsize) |
- result = malloc_bytes(size); |
- else |
- result = malloc_pages(size); |
-#ifdef SANITY |
- if (malloc_abort && !result) |
- wrterror("malloc() returns NULL\n"); |
-#endif |
- TRACE(("%6d M %p %d\n",malloc_event++,result,size)); |
- |
- return result; |
-} |
- |
-void *_PR_UnlockedMemalign(size_t alignment, size_t size) |
-{ |
- void *result; |
- |
- /* |
- * alignment has to be a power of 2 |
- */ |
- |
- if ((size <= alignment) && (alignment <= malloc_maxsize)) |
- size = alignment; |
- else |
- size += alignment - 1; |
- |
- /* Round up to a multiple of 8 bytes */ |
- if (size & 7) { |
- size = size + 8 - (size & 7); |
- } |
- |
- if (!initialized) |
- malloc_init(); |
- |
-#ifdef SANITY |
- if (suicide) |
- abort(); |
-#endif |
- |
- if (size <= malloc_maxsize) |
- result = malloc_bytes(size); |
- else |
- result = malloc_pages(size); |
-#ifdef SANITY |
- if (malloc_abort && !result) |
- wrterror("malloc() returns NULL\n"); |
-#endif |
- TRACE(("%6d A %p %d\n",malloc_event++,result,size)); |
- |
- if ((u_long)result & (alignment - 1)) |
- return ((void *)(((u_long)result + alignment) & ~(alignment - 1))); |
- else |
- return result; |
-} |
- |
-void *_PR_UnlockedCalloc(size_t n, size_t nelem) |
-{ |
- void *p; |
- |
- /* Compute total size and then round up to a double word amount */ |
- n *= nelem; |
- if (n & 7) { |
- n = n + 8 - (n & 7); |
- } |
- |
- /* Get the memory */ |
- p = _PR_UnlockedMalloc(n); |
- if (p) { |
- /* Zero it */ |
- memset(p, 0, n); |
- } |
- return p; |
-} |
- |
-/* |
- * Change an allocation's size |
- */ |
-void *_PR_UnlockedRealloc(void *ptr, size_t size) |
-{ |
- void *p; |
- u_long osize,page,index,tmp_index; |
- struct pginfo **mp; |
- |
- if (!initialized) |
- malloc_init(); |
- |
-#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; |
- } |
- } else if (*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 |
- else if (malloc_abort) |
- wrterror("realloc() returns NULL\n"); |
-#endif |
- |
- return p; |
-} |
- |
-/* |
- * Free a sequence of pages |
- */ |
- |
-static void |
-free_pages(char *ptr, u_long page, int index, struct pginfo *info) |
-{ |
- int i; |
- struct pgfree *pf,*pt; |
- u_long l; |
- char *tail; |
- |
- 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; |
- |
- l = i << malloc_pageshift; |
- |
- tail = ptr+l; |
- |
- /* add to free-list */ |
- if (!px) |
- px = (struct pgfree*)_PR_UnlockedMalloc(sizeof *pt); |
- /* XXX check success */ |
- px->page = ptr; |
- px->end = tail; |
- px->size = l; |
- if (!free_list.next) { |
- px->next = free_list.next; |
- px->prev = &free_list; |
- free_list.next = px; |
- pf = px; |
- px = 0; |
- } else { |
- tail = ptr+l; |
- for(pf = free_list.next; pf->next && pf->end < ptr; pf = pf->next) |
- ; |
- for(; pf; pf = pf->next) { |
- if (pf->end == ptr ) { |
- /* append to entry */ |
- pf->end += l; |
- pf->size += l; |
- if (pf->next && pf->end == pf->next->page ) { |
- pt = pf->next; |
- pf->end = pt->end; |
- pf->size += pt->size; |
- pf->next = pt->next; |
- if (pf->next) |
- pf->next->prev = pf; |
- _PR_UnlockedFree(pt); |
- } |
- } else if (pf->page == tail) { |
- /* prepend to entry */ |
- pf->size += l; |
- pf->page = ptr; |
- } else if (pf->page > ptr) { |
- px->next = pf; |
- px->prev = pf->prev; |
- pf->prev = px; |
- px->prev->next = px; |
- pf = px; |
- px = 0; |
- } else if (!pf->next) { |
- px->next = 0; |
- px->prev = pf; |
- pf->next = px; |
- pf = px; |
- px = 0; |
- } else { |
- continue; |
- } |
- break; |
- } |
- } |
- if (!pf->next && |
- pf->size > malloc_cache && |
- pf->end == malloc_brk && |
- malloc_brk == (void*)sbrk(0)) { |
- pf->end = pf->page + malloc_cache; |
- pf->size = malloc_cache; |
- TRACE(("%6d U %p %d\n",malloc_event++,pf->end,pf->end - pf->page)); |
- brk(pf->end); |
- malloc_brk = pf->end; |
- /* Find the page directory entry for the page in question */ |
- page = (u_long)pf->end >> malloc_pageshift; |
- index = page - malloc_origo; |
- /* Now update the directory */ |
- for(i=index;i <= last_index;) |
- page_dir[i++] = MALLOC_NOT_MINE; |
- last_index = index - 1; |
- } |
-} |
- |
-/* |
- * Free a chunk, and possibly the page it's on, if the page becomes empty. |
- */ |
- |
-static void |
-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); |
- } |
-} |
- |
-void _PR_UnlockedFree(void *ptr) |
-{ |
- u_long page; |
- struct pginfo *info; |
- int index, tmp_index; |
- |
- 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 */ |