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