andre@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #include "primpl.h" andre@0: andre@0: /* andre@0: ** We override malloc etc. on any platform which has preemption + andre@0: ** nspr20 user level threads. When we're debugging, we can make our andre@0: ** version of malloc fail occasionally. andre@0: */ andre@0: #ifdef _PR_OVERRIDE_MALLOC andre@0: andre@0: /* andre@0: ** Thread safe version of malloc, calloc, realloc, free andre@0: */ andre@0: #include andre@0: andre@0: #ifdef DEBUG andre@0: #define SANITY andre@0: #define EXTRA_SANITY andre@0: #else andre@0: #undef SANITY andre@0: #undef EXTRA_SANITY andre@0: #endif andre@0: andre@0: /* Forward decls */ andre@0: void *_PR_UnlockedMalloc(size_t size); andre@0: void _PR_UnlockedFree(void *ptr); andre@0: void *_PR_UnlockedRealloc(void *ptr, size_t size); andre@0: void *_PR_UnlockedCalloc(size_t n, size_t elsize); andre@0: andre@0: /************************************************************************/ andre@0: andre@0: /* andre@0: * ---------------------------------------------------------------------------- andre@0: * "THE BEER-WARE LICENSE" (Revision 42): andre@0: * wrote this file. As long as you retain this notice you andre@0: * can do whatever you want with this stuff. If we meet some day, and you think andre@0: * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp andre@0: * ---------------------------------------------------------------------------- andre@0: * andre@0: */ andre@0: andre@0: /* andre@0: * Defining SANITY will enable some checks which will tell you if the users andre@0: * program did botch something andre@0: */ andre@0: andre@0: /* andre@0: * Defining EXTRA_SANITY will enable some checks which are mostly related andre@0: * to internal conditions in malloc.c andre@0: */ andre@0: andre@0: /* andre@0: * Very verbose progress on stdout... andre@0: */ andre@0: #if 0 andre@0: # define TRACE(foo) printf foo andre@0: static int malloc_event; andre@0: #else andre@0: # define TRACE(foo) andre@0: #endif andre@0: andre@0: /* XXX Pick a number, any number */ andre@0: # define malloc_pagesize 4096UL andre@0: # define malloc_pageshift 12UL andre@0: andre@0: #ifdef XP_UNIX andre@0: #include andre@0: #include andre@0: #include andre@0: #include andre@0: #include andre@0: #include andre@0: #include andre@0: #endif andre@0: andre@0: /* andre@0: * This structure describes a page's worth of chunks. andre@0: */ andre@0: andre@0: struct pginfo { andre@0: struct pginfo *next; /* next on the free list */ andre@0: char *page; /* Pointer to the page */ andre@0: u_short size; /* size of this page's chunks */ andre@0: u_short shift; /* How far to shift for this size chunks */ andre@0: u_short free; /* How many free chunks */ andre@0: u_short total; /* How many chunk */ andre@0: u_long bits[1]; /* Which chunks are free */ andre@0: }; andre@0: andre@0: struct pgfree { andre@0: struct pgfree *next; /* next run of free pages */ andre@0: struct pgfree *prev; /* prev run of free pages */ andre@0: char *page; /* pointer to free pages */ andre@0: char *end; /* pointer to end of free pages */ andre@0: u_long size; /* number of bytes free */ andre@0: }; andre@0: andre@0: /* andre@0: * How many bits per u_long in the bitmap. andre@0: * Change only if not 8 bits/byte andre@0: */ andre@0: #define MALLOC_BITS (8*sizeof(u_long)) andre@0: andre@0: /* andre@0: * Magic values to put in the page_directory andre@0: */ andre@0: #define MALLOC_NOT_MINE ((struct pginfo*) 0) andre@0: #define MALLOC_FREE ((struct pginfo*) 1) andre@0: #define MALLOC_FIRST ((struct pginfo*) 2) andre@0: #define MALLOC_FOLLOW ((struct pginfo*) 3) andre@0: #define MALLOC_MAGIC ((struct pginfo*) 4) andre@0: andre@0: /* andre@0: * Set to one when malloc_init has been called andre@0: */ andre@0: static unsigned initialized; andre@0: andre@0: /* andre@0: * The size of a page. andre@0: * Must be a integral multiplum of the granularity of mmap(2). andre@0: * Your toes will curl if it isn't a power of two andre@0: */ andre@0: #define malloc_pagemask ((malloc_pagesize)-1) andre@0: andre@0: /* andre@0: * The size of the largest chunk. andre@0: * Half a page. andre@0: */ andre@0: #define malloc_maxsize ((malloc_pagesize)>>1) andre@0: andre@0: /* andre@0: * malloc_pagesize == 1 << malloc_pageshift andre@0: */ andre@0: #ifndef malloc_pageshift andre@0: static unsigned malloc_pageshift; andre@0: #endif /* malloc_pageshift */ andre@0: andre@0: /* andre@0: * The smallest allocation we bother about. andre@0: * Must be power of two andre@0: */ andre@0: #ifndef malloc_minsize andre@0: static unsigned malloc_minsize; andre@0: #endif /* malloc_minsize */ andre@0: andre@0: /* andre@0: * The largest chunk we care about. andre@0: * Must be smaller than pagesize andre@0: * Must be power of two andre@0: */ andre@0: #ifndef malloc_maxsize andre@0: static unsigned malloc_maxsize; andre@0: #endif /* malloc_maxsize */ andre@0: andre@0: #ifndef malloc_cache andre@0: static unsigned malloc_cache; andre@0: #endif /* malloc_cache */ andre@0: andre@0: /* andre@0: * The offset from pagenumber to index into the page directory andre@0: */ andre@0: static u_long malloc_origo; andre@0: andre@0: /* andre@0: * The last index in the page directory we care about andre@0: */ andre@0: static u_long last_index; andre@0: andre@0: /* andre@0: * Pointer to page directory. andre@0: * Allocated "as if with" malloc andre@0: */ andre@0: static struct pginfo **page_dir; andre@0: andre@0: /* andre@0: * How many slots in the page directory andre@0: */ andre@0: static unsigned malloc_ninfo; andre@0: andre@0: /* andre@0: * Free pages line up here andre@0: */ andre@0: static struct pgfree free_list; andre@0: andre@0: /* andre@0: * Abort() if we fail to get VM ? andre@0: */ andre@0: static int malloc_abort; andre@0: andre@0: #ifdef SANITY andre@0: /* andre@0: * Are we trying to die ? andre@0: */ andre@0: static int suicide; andre@0: #endif andre@0: andre@0: /* andre@0: * dump statistics andre@0: */ andre@0: static int malloc_stats; andre@0: andre@0: /* andre@0: * always realloc ? andre@0: */ andre@0: static int malloc_realloc; andre@0: andre@0: /* andre@0: * my last break. andre@0: */ andre@0: static void *malloc_brk; andre@0: andre@0: /* andre@0: * one location cache for free-list holders andre@0: */ andre@0: static struct pgfree *px; andre@0: andre@0: static int set_pgdir(void *ptr, struct pginfo *info); andre@0: static int extend_page_directory(u_long index); andre@0: andre@0: #ifdef SANITY andre@0: void andre@0: malloc_dump(FILE *fd) andre@0: { andre@0: struct pginfo **pd; andre@0: struct pgfree *pf; andre@0: int j; andre@0: andre@0: pd = page_dir; andre@0: andre@0: /* print out all the pages */ andre@0: for(j=0;j<=last_index;j++) { andre@0: fprintf(fd,"%08lx %5d ",(j+malloc_origo) << malloc_pageshift,j); andre@0: if (pd[j] == MALLOC_NOT_MINE) { andre@0: for(j++;j<=last_index && pd[j] == MALLOC_NOT_MINE;j++) andre@0: ; andre@0: j--; andre@0: fprintf(fd,".. %5d not mine\n", j); andre@0: } else if (pd[j] == MALLOC_FREE) { andre@0: for(j++;j<=last_index && pd[j] == MALLOC_FREE;j++) andre@0: ; andre@0: j--; andre@0: fprintf(fd,".. %5d free\n", j); andre@0: } else if (pd[j] == MALLOC_FIRST) { andre@0: for(j++;j<=last_index && pd[j] == MALLOC_FOLLOW;j++) andre@0: ; andre@0: j--; andre@0: fprintf(fd,".. %5d in use\n", j); andre@0: } else if (pd[j] < MALLOC_MAGIC) { andre@0: fprintf(fd,"(%p)\n", pd[j]); andre@0: } else { andre@0: fprintf(fd,"%p %d (of %d) x %d @ %p --> %p\n", andre@0: pd[j],pd[j]->free, pd[j]->total, andre@0: pd[j]->size, pd[j]->page, pd[j]->next); andre@0: } andre@0: } andre@0: andre@0: for(pf=free_list.next; pf; pf=pf->next) { andre@0: fprintf(fd,"Free: @%p [%p...%p[ %ld ->%p <-%p\n", andre@0: pf,pf->page,pf->end,pf->size,pf->prev,pf->next); andre@0: if (pf == pf->next) { andre@0: fprintf(fd,"Free_list loops.\n"); andre@0: break; andre@0: } andre@0: } andre@0: andre@0: /* print out various info */ andre@0: fprintf(fd,"Minsize\t%d\n",malloc_minsize); andre@0: fprintf(fd,"Maxsize\t%ld\n",malloc_maxsize); andre@0: fprintf(fd,"Pagesize\t%ld\n",malloc_pagesize); andre@0: fprintf(fd,"Pageshift\t%ld\n",malloc_pageshift); andre@0: fprintf(fd,"FirstPage\t%ld\n",malloc_origo); andre@0: fprintf(fd,"LastPage\t%ld %lx\n",last_index+malloc_pageshift, andre@0: (last_index + malloc_pageshift) << malloc_pageshift); andre@0: fprintf(fd,"Break\t%ld\n",(u_long)sbrk(0) >> malloc_pageshift); andre@0: } andre@0: andre@0: static void wrterror(char *fmt, ...) andre@0: { andre@0: char *q = "malloc() error: "; andre@0: char buf[100]; andre@0: va_list ap; andre@0: andre@0: suicide = 1; andre@0: andre@0: va_start(ap, fmt); andre@0: PR_vsnprintf(buf, sizeof(buf), fmt, ap); andre@0: va_end(ap); andre@0: fputs(q, stderr); andre@0: fputs(buf, stderr); andre@0: andre@0: malloc_dump(stderr); andre@0: PR_Abort(); andre@0: } andre@0: andre@0: static void wrtwarning(char *fmt, ...) andre@0: { andre@0: char *q = "malloc() warning: "; andre@0: char buf[100]; andre@0: va_list ap; andre@0: andre@0: va_start(ap, fmt); andre@0: PR_vsnprintf(buf, sizeof(buf), fmt, ap); andre@0: va_end(ap); andre@0: fputs(q, stderr); andre@0: fputs(buf, stderr); andre@0: } andre@0: #endif /* SANITY */ andre@0: andre@0: andre@0: /* andre@0: * Allocate a number of pages from the OS andre@0: */ andre@0: static caddr_t andre@0: map_pages(int pages, int update) andre@0: { andre@0: caddr_t result,tail; andre@0: andre@0: result = ((caddr_t)sbrk(0)) + malloc_pagemask - 1; andre@0: result = (caddr_t) ((u_long)result & ~malloc_pagemask); andre@0: tail = result + (pages << malloc_pageshift); andre@0: if (!brk(tail)) { andre@0: last_index = ((u_long)tail >> malloc_pageshift) - malloc_origo -1; andre@0: malloc_brk = tail; andre@0: TRACE(("%6d S %p .. %p\n",malloc_event++, result, tail)); andre@0: if (!update || last_index < malloc_ninfo || andre@0: extend_page_directory(last_index)) andre@0: return result; andre@0: } andre@0: TRACE(("%6d s %d %p %d\n",malloc_event++,pages,sbrk(0),errno)); andre@0: #ifdef EXTRA_SANITY andre@0: wrterror("map_pages fails\n"); andre@0: #endif andre@0: return 0; andre@0: } andre@0: andre@0: #define set_bit(_pi,_bit) \ andre@0: (_pi)->bits[(_bit)/MALLOC_BITS] |= 1L<<((_bit)%MALLOC_BITS) andre@0: andre@0: #define clr_bit(_pi,_bit) \ andre@0: (_pi)->bits[(_bit)/MALLOC_BITS] &= ~(1L<<((_bit)%MALLOC_BITS)); andre@0: andre@0: #define tst_bit(_pi,_bit) \ andre@0: ((_pi)->bits[(_bit)/MALLOC_BITS] & (1L<<((_bit)%MALLOC_BITS))) andre@0: andre@0: /* andre@0: * Extend page directory andre@0: */ andre@0: static int andre@0: extend_page_directory(u_long index) andre@0: { andre@0: struct pginfo **young, **old; andre@0: int i; andre@0: andre@0: TRACE(("%6d E %lu\n",malloc_event++,index)); andre@0: andre@0: /* Make it this many pages */ andre@0: i = index * sizeof *page_dir; andre@0: i /= malloc_pagesize; andre@0: i += 2; andre@0: andre@0: /* Get new pages, if you used this much mem you don't care :-) */ andre@0: young = (struct pginfo**) map_pages(i,0); andre@0: if (!young) andre@0: return 0; andre@0: andre@0: /* Copy the old stuff */ andre@0: memset(young, 0, i * malloc_pagesize); andre@0: memcpy(young, page_dir, andre@0: malloc_ninfo * sizeof *page_dir); andre@0: andre@0: /* register the new size */ andre@0: malloc_ninfo = i * malloc_pagesize / sizeof *page_dir; andre@0: andre@0: /* swap the pointers */ andre@0: old = page_dir; andre@0: page_dir = young; andre@0: andre@0: /* Mark the pages */ andre@0: index = ((u_long)young >> malloc_pageshift) - malloc_origo; andre@0: page_dir[index] = MALLOC_FIRST; andre@0: while (--i) { andre@0: page_dir[++index] = MALLOC_FOLLOW; andre@0: } andre@0: andre@0: /* Now free the old stuff */ andre@0: _PR_UnlockedFree(old); andre@0: return 1; andre@0: } andre@0: andre@0: /* andre@0: * Set entry in page directory. andre@0: * Extend page directory if need be. andre@0: */ andre@0: static int andre@0: set_pgdir(void *ptr, struct pginfo *info) andre@0: { andre@0: u_long index = ((u_long)ptr >> malloc_pageshift) - malloc_origo; andre@0: andre@0: if (index >= malloc_ninfo && !extend_page_directory(index)) andre@0: return 0; andre@0: page_dir[index] = info; andre@0: return 1; andre@0: } andre@0: andre@0: /* andre@0: * Initialize the world andre@0: */ andre@0: static void andre@0: malloc_init (void) andre@0: { andre@0: int i; andre@0: char *p; andre@0: andre@0: TRACE(("%6d I\n",malloc_event++)); andre@0: #ifdef DEBUG andre@0: for (p=getenv("MALLOC_OPTIONS"); p && *p; p++) { andre@0: switch (*p) { andre@0: case 'a': malloc_abort = 0; break; andre@0: case 'A': malloc_abort = 1; break; andre@0: case 'd': malloc_stats = 0; break; andre@0: case 'D': malloc_stats = 1; break; andre@0: case 'r': malloc_realloc = 0; break; andre@0: case 'R': malloc_realloc = 1; break; andre@0: default: andre@0: wrtwarning("Unknown chars in MALLOC_OPTIONS\n"); andre@0: break; andre@0: } andre@0: } andre@0: #endif andre@0: andre@0: #ifndef malloc_pagesize andre@0: /* determine our pagesize */ andre@0: malloc_pagesize = getpagesize(); andre@0: #endif /* malloc_pagesize */ andre@0: andre@0: #ifndef malloc_pageshift andre@0: /* determine how much we shift by to get there */ andre@0: for (i = malloc_pagesize; i > 1; i >>= 1) andre@0: malloc_pageshift++; andre@0: #endif /* malloc_pageshift */ andre@0: andre@0: #ifndef malloc_cache andre@0: malloc_cache = 50 << malloc_pageshift; andre@0: #endif /* malloc_cache */ andre@0: andre@0: #ifndef malloc_minsize andre@0: /* andre@0: * find the smallest size allocation we will bother about. andre@0: * this is determined as the smallest allocation that can hold andre@0: * it's own pginfo; andre@0: */ andre@0: i = 2; andre@0: for(;;) { andre@0: int j; andre@0: andre@0: /* Figure out the size of the bits */ andre@0: j = malloc_pagesize/i; andre@0: j /= 8; andre@0: if (j < sizeof(u_long)) andre@0: j = sizeof (u_long); andre@0: if (sizeof(struct pginfo) + j - sizeof (u_long) <= i) andre@0: break; andre@0: i += i; andre@0: } andre@0: malloc_minsize = i; andre@0: #endif /* malloc_minsize */ andre@0: andre@0: andre@0: /* Allocate one page for the page directory */ andre@0: page_dir = (struct pginfo **) map_pages(1,0); andre@0: #ifdef SANITY andre@0: if (!page_dir) andre@0: wrterror("fatal: my first mmap failed. (check limits ?)\n"); andre@0: #endif andre@0: andre@0: /* andre@0: * We need a maximum of malloc_pageshift buckets, steal these from the andre@0: * front of the page_directory; andre@0: */ andre@0: malloc_origo = (u_long) page_dir >> malloc_pageshift; andre@0: malloc_origo -= malloc_pageshift; andre@0: andre@0: /* Clear it */ andre@0: memset(page_dir,0,malloc_pagesize); andre@0: andre@0: /* Find out how much it tells us */ andre@0: malloc_ninfo = malloc_pagesize / sizeof *page_dir; andre@0: andre@0: /* Plug the page directory into itself */ andre@0: i = set_pgdir(page_dir,MALLOC_FIRST); andre@0: #ifdef SANITY andre@0: if (!i) andre@0: wrterror("fatal: couldn't set myself in the page directory\n"); andre@0: #endif andre@0: andre@0: /* Been here, done that */ andre@0: initialized++; andre@0: } andre@0: andre@0: /* andre@0: * Allocate a number of complete pages andre@0: */ andre@0: static void *malloc_pages(size_t size) andre@0: { andre@0: void *p,*delay_free = 0; andre@0: int i; andre@0: struct pgfree *pf; andre@0: u_long index; andre@0: andre@0: /* How many pages ? */ andre@0: size += (malloc_pagesize-1); andre@0: size &= ~malloc_pagemask; andre@0: andre@0: p = 0; andre@0: /* Look for free pages before asking for more */ andre@0: for(pf = free_list.next; pf; pf = pf->next) { andre@0: #ifdef EXTRA_SANITY andre@0: if (pf->page == pf->end) andre@0: wrterror("zero entry on free_list\n"); andre@0: if (pf->page > pf->end) { andre@0: TRACE(("%6d !s %p %p %p <%d>\n",malloc_event++, andre@0: pf,pf->page,pf->end,__LINE__)); andre@0: wrterror("sick entry on free_list\n"); andre@0: } andre@0: if ((void*)pf->page >= (void*)sbrk(0)) andre@0: wrterror("entry on free_list past brk\n"); andre@0: if (page_dir[((u_long)pf->page >> malloc_pageshift) - malloc_origo] andre@0: != MALLOC_FREE) { andre@0: TRACE(("%6d !f %p %p %p <%d>\n",malloc_event++, andre@0: pf,pf->page,pf->end,__LINE__)); andre@0: wrterror("non-free first page on free-list\n"); andre@0: } andre@0: if (page_dir[((u_long)pf->end >> malloc_pageshift) - 1 - malloc_origo] andre@0: != MALLOC_FREE) andre@0: wrterror("non-free last page on free-list\n"); andre@0: #endif /* EXTRA_SANITY */ andre@0: if (pf->size < size) andre@0: continue; andre@0: else if (pf->size == size) { andre@0: p = pf->page; andre@0: if (pf->next) andre@0: pf->next->prev = pf->prev; andre@0: pf->prev->next = pf->next; andre@0: delay_free = pf; andre@0: break; andre@0: } else { andre@0: p = pf->page; andre@0: pf->page += size; andre@0: pf->size -= size; andre@0: break; andre@0: } andre@0: } andre@0: #ifdef EXTRA_SANITY andre@0: if (p && page_dir[((u_long)p >> malloc_pageshift) - malloc_origo] andre@0: != MALLOC_FREE) { andre@0: wrterror("allocated non-free page on free-list\n"); andre@0: } andre@0: #endif /* EXTRA_SANITY */ andre@0: andre@0: size >>= malloc_pageshift; andre@0: andre@0: /* Map new pages */ andre@0: if (!p) andre@0: p = map_pages(size,1); andre@0: andre@0: if (p) { andre@0: /* Mark the pages in the directory */ andre@0: index = ((u_long)p >> malloc_pageshift) - malloc_origo; andre@0: page_dir[index] = MALLOC_FIRST; andre@0: for (i=1;i> bits)+MALLOC_BITS-1) / MALLOC_BITS); andre@0: if ((1<<(bits)) <= l+l) { andre@0: bp = (struct pginfo *)pp; andre@0: } else { andre@0: bp = (struct pginfo *)_PR_UnlockedMalloc(l); andre@0: } andre@0: if (!bp) andre@0: return 0; andre@0: bp->size = (1<shift = bits; andre@0: bp->total = bp->free = malloc_pagesize >> bits; andre@0: bp->next = page_dir[bits]; andre@0: bp->page = (char*)pp; andre@0: i = set_pgdir(pp,bp); andre@0: if (!i) andre@0: return 0; andre@0: andre@0: /* We can safely assume that there is nobody in this chain */ andre@0: page_dir[bits] = bp; andre@0: andre@0: /* set all valid bits in the bits */ andre@0: k = bp->total; andre@0: i = 0; andre@0: /* andre@0: for(;k-i >= MALLOC_BITS; i += MALLOC_BITS) andre@0: bp->bits[i / MALLOC_BITS] = ~0; andre@0: */ andre@0: for(; i < k; i++) andre@0: set_bit(bp,i); andre@0: andre@0: if (bp != pp) andre@0: return 1; andre@0: andre@0: /* We may have used the first ones already */ andre@0: for(i=0;l > 0;i++) { andre@0: clr_bit(bp,i); andre@0: bp->free--; andre@0: bp->total--; andre@0: l -= (1 << bits); andre@0: } andre@0: return 1; andre@0: } andre@0: andre@0: /* andre@0: * Allocate a fragment andre@0: */ andre@0: static void *malloc_bytes(size_t size) andre@0: { andre@0: size_t s; andre@0: int j; andre@0: struct pginfo *bp; andre@0: int k; andre@0: u_long *lp, bf; andre@0: andre@0: /* Don't bother with anything less than this */ andre@0: if (size < malloc_minsize) { andre@0: size = malloc_minsize; andre@0: } andre@0: andre@0: /* Find the right bucket */ andre@0: j = 1; andre@0: s = size - 1; andre@0: while (s >>= 1) { andre@0: j++; andre@0: } andre@0: andre@0: /* If it's empty, make a page more of that size chunks */ andre@0: if (!page_dir[j] && !malloc_make_chunks(j)) andre@0: return 0; andre@0: andre@0: /* Find first word of bitmap which isn't empty */ andre@0: bp = page_dir[j]; andre@0: for (lp = bp->bits; !*lp; lp++) andre@0: ; andre@0: andre@0: /* Find that bit */ andre@0: bf = *lp; andre@0: k = 0; andre@0: while ((bf & 1) == 0) { andre@0: bf >>= 1; andre@0: k++; andre@0: } andre@0: andre@0: *lp ^= 1L<free--; andre@0: if (!bp->free) { andre@0: page_dir[j] = bp->next; andre@0: bp->next = 0; andre@0: } andre@0: k += (lp - bp->bits)*MALLOC_BITS; andre@0: return bp->page + (k << bp->shift); andre@0: } andre@0: andre@0: void *_PR_UnlockedMalloc(size_t size) andre@0: { andre@0: void *result; andre@0: andre@0: /* Round up to a multiple of 8 bytes */ andre@0: if (size & 7) { andre@0: size = size + 8 - (size & 7); andre@0: } andre@0: andre@0: if (!initialized) andre@0: malloc_init(); andre@0: andre@0: #ifdef SANITY andre@0: if (suicide) andre@0: PR_Abort(); andre@0: #endif andre@0: andre@0: if (size <= malloc_maxsize) andre@0: result = malloc_bytes(size); andre@0: else andre@0: result = malloc_pages(size); andre@0: #ifdef SANITY andre@0: if (malloc_abort && !result) andre@0: wrterror("malloc() returns NULL\n"); andre@0: #endif andre@0: TRACE(("%6d M %p %d\n",malloc_event++,result,size)); andre@0: andre@0: return result; andre@0: } andre@0: andre@0: void *_PR_UnlockedMemalign(size_t alignment, size_t size) andre@0: { andre@0: void *result; andre@0: andre@0: /* andre@0: * alignment has to be a power of 2 andre@0: */ andre@0: andre@0: if ((size <= alignment) && (alignment <= malloc_maxsize)) andre@0: size = alignment; andre@0: else andre@0: size += alignment - 1; andre@0: andre@0: /* Round up to a multiple of 8 bytes */ andre@0: if (size & 7) { andre@0: size = size + 8 - (size & 7); andre@0: } andre@0: andre@0: if (!initialized) andre@0: malloc_init(); andre@0: andre@0: #ifdef SANITY andre@0: if (suicide) andre@0: abort(); andre@0: #endif andre@0: andre@0: if (size <= malloc_maxsize) andre@0: result = malloc_bytes(size); andre@0: else andre@0: result = malloc_pages(size); andre@0: #ifdef SANITY andre@0: if (malloc_abort && !result) andre@0: wrterror("malloc() returns NULL\n"); andre@0: #endif andre@0: TRACE(("%6d A %p %d\n",malloc_event++,result,size)); andre@0: andre@0: if ((u_long)result & (alignment - 1)) andre@0: return ((void *)(((u_long)result + alignment) & ~(alignment - 1))); andre@0: else andre@0: return result; andre@0: } andre@0: andre@0: void *_PR_UnlockedCalloc(size_t n, size_t nelem) andre@0: { andre@0: void *p; andre@0: andre@0: /* Compute total size and then round up to a double word amount */ andre@0: n *= nelem; andre@0: if (n & 7) { andre@0: n = n + 8 - (n & 7); andre@0: } andre@0: andre@0: /* Get the memory */ andre@0: p = _PR_UnlockedMalloc(n); andre@0: if (p) { andre@0: /* Zero it */ andre@0: memset(p, 0, n); andre@0: } andre@0: return p; andre@0: } andre@0: andre@0: /* andre@0: * Change an allocation's size andre@0: */ andre@0: void *_PR_UnlockedRealloc(void *ptr, size_t size) andre@0: { andre@0: void *p; andre@0: u_long osize,page,index,tmp_index; andre@0: struct pginfo **mp; andre@0: andre@0: if (!initialized) andre@0: malloc_init(); andre@0: andre@0: #ifdef SANITY andre@0: if (suicide) andre@0: PR_Abort(); andre@0: #endif andre@0: andre@0: /* used as free() */ andre@0: TRACE(("%6d R %p %d\n",malloc_event++, ptr, size)); andre@0: if (ptr && !size) { andre@0: _PR_UnlockedFree(ptr); andre@0: return _PR_UnlockedMalloc (1); andre@0: } andre@0: andre@0: /* used as malloc() */ andre@0: if (!ptr) { andre@0: p = _PR_UnlockedMalloc(size); andre@0: return p; andre@0: } andre@0: andre@0: /* Find the page directory entry for the page in question */ andre@0: page = (u_long)ptr >> malloc_pageshift; andre@0: index = page - malloc_origo; andre@0: andre@0: /* andre@0: * check if memory was allocated by memalign andre@0: */ andre@0: tmp_index = index; andre@0: while (page_dir[tmp_index] == MALLOC_FOLLOW) andre@0: tmp_index--; andre@0: if (tmp_index != index) { andre@0: /* andre@0: * memalign-allocated memory andre@0: */ andre@0: index = tmp_index; andre@0: page = index + malloc_origo; andre@0: ptr = (void *) (page << malloc_pageshift); andre@0: } andre@0: TRACE(("%6d R2 %p %d\n",malloc_event++, ptr, size)); andre@0: andre@0: /* make sure it makes sense in some fashion */ andre@0: if (index < malloc_pageshift || index > last_index) { andre@0: #ifdef SANITY andre@0: wrtwarning("junk pointer passed to realloc()\n"); andre@0: #endif andre@0: return 0; andre@0: } andre@0: andre@0: /* find the size of that allocation, and see if we need to relocate */ andre@0: mp = &page_dir[index]; andre@0: if (*mp == MALLOC_FIRST) { andre@0: osize = malloc_pagesize; andre@0: while (mp[1] == MALLOC_FOLLOW) { andre@0: osize += malloc_pagesize; andre@0: mp++; andre@0: } andre@0: if (!malloc_realloc && andre@0: size < osize && andre@0: size > malloc_maxsize && andre@0: size > (osize - malloc_pagesize)) { andre@0: return ptr; andre@0: } andre@0: } else if (*mp >= MALLOC_MAGIC) { andre@0: osize = (*mp)->size; andre@0: if (!malloc_realloc && andre@0: size < osize && andre@0: (size > (*mp)->size/2 || (*mp)->size == malloc_minsize)) { andre@0: return ptr; andre@0: } andre@0: } else { andre@0: #ifdef SANITY andre@0: wrterror("realloc() of wrong page.\n"); andre@0: #endif andre@0: } andre@0: andre@0: /* try to reallocate */ andre@0: p = _PR_UnlockedMalloc(size); andre@0: andre@0: if (p) { andre@0: /* copy the lesser of the two sizes */ andre@0: if (osize < size) andre@0: memcpy(p,ptr,osize); andre@0: else andre@0: memcpy(p,ptr,size); andre@0: _PR_UnlockedFree(ptr); andre@0: } andre@0: #ifdef DEBUG andre@0: else if (malloc_abort) andre@0: wrterror("realloc() returns NULL\n"); andre@0: #endif andre@0: andre@0: return p; andre@0: } andre@0: andre@0: /* andre@0: * Free a sequence of pages andre@0: */ andre@0: andre@0: static void andre@0: free_pages(char *ptr, u_long page, int index, struct pginfo *info) andre@0: { andre@0: int i; andre@0: struct pgfree *pf,*pt; andre@0: u_long l; andre@0: char *tail; andre@0: andre@0: TRACE(("%6d FP %p %d\n",malloc_event++, ptr, page)); andre@0: /* Is it free already ? */ andre@0: if (info == MALLOC_FREE) { andre@0: #ifdef SANITY andre@0: wrtwarning("freeing free page at %p.\n", ptr); andre@0: #endif andre@0: return; andre@0: } andre@0: andre@0: #ifdef SANITY andre@0: /* Is it not the right place to begin ? */ andre@0: if (info != MALLOC_FIRST) andre@0: wrterror("freeing wrong page.\n"); andre@0: andre@0: /* Is this really a pointer to a page ? */ andre@0: if ((u_long)ptr & malloc_pagemask) andre@0: wrterror("freeing messed up page pointer.\n"); andre@0: #endif andre@0: andre@0: /* Count how many pages it is anyway */ andre@0: page_dir[index] = MALLOC_FREE; andre@0: for (i = 1; page_dir[index+i] == MALLOC_FOLLOW; i++) andre@0: page_dir[index + i] = MALLOC_FREE; andre@0: andre@0: l = i << malloc_pageshift; andre@0: andre@0: tail = ptr+l; andre@0: andre@0: /* add to free-list */ andre@0: if (!px) andre@0: px = (struct pgfree*)_PR_UnlockedMalloc(sizeof *pt); andre@0: /* XXX check success */ andre@0: px->page = ptr; andre@0: px->end = tail; andre@0: px->size = l; andre@0: if (!free_list.next) { andre@0: px->next = free_list.next; andre@0: px->prev = &free_list; andre@0: free_list.next = px; andre@0: pf = px; andre@0: px = 0; andre@0: } else { andre@0: tail = ptr+l; andre@0: for(pf = free_list.next; pf->next && pf->end < ptr; pf = pf->next) andre@0: ; andre@0: for(; pf; pf = pf->next) { andre@0: if (pf->end == ptr ) { andre@0: /* append to entry */ andre@0: pf->end += l; andre@0: pf->size += l; andre@0: if (pf->next && pf->end == pf->next->page ) { andre@0: pt = pf->next; andre@0: pf->end = pt->end; andre@0: pf->size += pt->size; andre@0: pf->next = pt->next; andre@0: if (pf->next) andre@0: pf->next->prev = pf; andre@0: _PR_UnlockedFree(pt); andre@0: } andre@0: } else if (pf->page == tail) { andre@0: /* prepend to entry */ andre@0: pf->size += l; andre@0: pf->page = ptr; andre@0: } else if (pf->page > ptr) { andre@0: px->next = pf; andre@0: px->prev = pf->prev; andre@0: pf->prev = px; andre@0: px->prev->next = px; andre@0: pf = px; andre@0: px = 0; andre@0: } else if (!pf->next) { andre@0: px->next = 0; andre@0: px->prev = pf; andre@0: pf->next = px; andre@0: pf = px; andre@0: px = 0; andre@0: } else { andre@0: continue; andre@0: } andre@0: break; andre@0: } andre@0: } andre@0: if (!pf->next && andre@0: pf->size > malloc_cache && andre@0: pf->end == malloc_brk && andre@0: malloc_brk == (void*)sbrk(0)) { andre@0: pf->end = pf->page + malloc_cache; andre@0: pf->size = malloc_cache; andre@0: TRACE(("%6d U %p %d\n",malloc_event++,pf->end,pf->end - pf->page)); andre@0: brk(pf->end); andre@0: malloc_brk = pf->end; andre@0: /* Find the page directory entry for the page in question */ andre@0: page = (u_long)pf->end >> malloc_pageshift; andre@0: index = page - malloc_origo; andre@0: /* Now update the directory */ andre@0: for(i=index;i <= last_index;) andre@0: page_dir[i++] = MALLOC_NOT_MINE; andre@0: last_index = index - 1; andre@0: } andre@0: } andre@0: andre@0: /* andre@0: * Free a chunk, and possibly the page it's on, if the page becomes empty. andre@0: */ andre@0: andre@0: static void andre@0: free_bytes(void *ptr, u_long page, int index, struct pginfo *info) andre@0: { andre@0: int i; andre@0: struct pginfo **mp; andre@0: void *vp; andre@0: andre@0: /* Make sure that pointer is multiplum of chunk-size */ andre@0: #ifdef SANITY andre@0: if ((u_long)ptr & (info->size - 1)) andre@0: wrterror("freeing messed up chunk pointer\n"); andre@0: #endif andre@0: andre@0: /* Find the chunk number on the page */ andre@0: i = ((u_long)ptr & malloc_pagemask) >> info->shift; andre@0: andre@0: /* See if it's free already */ andre@0: if (tst_bit(info,i)) { andre@0: #ifdef SANITY andre@0: wrtwarning("freeing free chunk at %p\n", ptr); andre@0: #endif andre@0: return; andre@0: } andre@0: andre@0: /* Mark it free */ andre@0: set_bit(info,i); andre@0: info->free++; andre@0: andre@0: /* If the page was full before, we need to put it on the queue now */ andre@0: if (info->free == 1) { andre@0: mp = page_dir + info->shift; andre@0: while (*mp && (*mp)->next && (*mp)->next->page < info->page) andre@0: mp = &(*mp)->next; andre@0: info->next = *mp; andre@0: *mp = info; andre@0: return; andre@0: } andre@0: andre@0: /* If this page isn't empty, don't do anything. */ andre@0: if (info->free != info->total) andre@0: return; andre@0: andre@0: /* We may want to keep at least one page of each size chunks around. */ andre@0: mp = page_dir + info->shift; andre@0: if (0 && (*mp == info) && !info->next) andre@0: return; andre@0: andre@0: /* Find & remove this page in the queue */ andre@0: while (*mp != info) { andre@0: mp = &((*mp)->next); andre@0: #ifdef EXTRA_SANITY andre@0: if (!*mp) { andre@0: TRACE(("%6d !q %p\n",malloc_event++,info)); andre@0: wrterror("Not on queue\n"); andre@0: } andre@0: #endif andre@0: } andre@0: *mp = info->next; andre@0: andre@0: /* Free the page & the info structure if need be */ andre@0: set_pgdir(info->page,MALLOC_FIRST); andre@0: if((void*)info->page == (void*)info) { andre@0: _PR_UnlockedFree(info->page); andre@0: } else { andre@0: vp = info->page; andre@0: _PR_UnlockedFree(info); andre@0: _PR_UnlockedFree(vp); andre@0: } andre@0: } andre@0: andre@0: void _PR_UnlockedFree(void *ptr) andre@0: { andre@0: u_long page; andre@0: struct pginfo *info; andre@0: int index, tmp_index; andre@0: andre@0: TRACE(("%6d F %p\n",malloc_event++,ptr)); andre@0: /* This is legal */ andre@0: if (!ptr) andre@0: return; andre@0: andre@0: #ifdef SANITY andre@0: /* There wouldn't be anything to free */ andre@0: if (!initialized) { andre@0: wrtwarning("free() called before malloc() ever got called\n"); andre@0: return; andre@0: } andre@0: #endif andre@0: andre@0: #ifdef SANITY andre@0: if (suicide) andre@0: PR_Abort(); andre@0: #endif andre@0: andre@0: /* Find the page directory entry for the page in question */ andre@0: page = (u_long)ptr >> malloc_pageshift; andre@0: index = page - malloc_origo; andre@0: andre@0: /* andre@0: * check if memory was allocated by memalign andre@0: */ andre@0: tmp_index = index; andre@0: while (page_dir[tmp_index] == MALLOC_FOLLOW) andre@0: tmp_index--; andre@0: if (tmp_index != index) { andre@0: /* andre@0: * memalign-allocated memory andre@0: */ andre@0: index = tmp_index; andre@0: page = index + malloc_origo; andre@0: ptr = (void *) (page << malloc_pageshift); andre@0: } andre@0: /* make sure it makes sense in some fashion */ andre@0: if (index < malloc_pageshift) { andre@0: #ifdef SANITY andre@0: wrtwarning("junk pointer %p (low) passed to free()\n", ptr); andre@0: #endif andre@0: return; andre@0: } andre@0: if (index > last_index) { andre@0: #ifdef SANITY andre@0: wrtwarning("junk pointer %p (high) passed to free()\n", ptr); andre@0: #endif andre@0: return; andre@0: } andre@0: andre@0: /* handle as page-allocation or chunk allocation */ andre@0: info = page_dir[index]; andre@0: if (info < MALLOC_MAGIC) andre@0: free_pages((char*)ptr, page, index, info); andre@0: else andre@0: free_bytes(ptr,page,index,info); andre@0: return; andre@0: } andre@0: #endif /* _PR_OVERRIDE_MALLOC */