/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape Portable Runtime (NSPR).
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#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
#endif
/* Forward decls */
void _PR_UnlockedFree(void *ptr);
/************************************************************************/
/*
* ----------------------------------------------------------------------------
* "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
static int malloc_event;
#else
#endif
/* XXX Pick a number, any number */
#ifdef XP_UNIX
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#endif
/*
* This structure describes a page's worth of chunks.
*/
struct pginfo {
};
struct pgfree {
};
/*
* How many bits per u_long in the bitmap.
*/
/*
* Magic values to put in the page_directory
*/
/*
* 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
*/
/*
* The size of the largest chunk.
* Half a page.
*/
/*
* 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
*/
/*
* The last index in the page directory we care about
*/
/*
* Pointer to page directory.
* Allocated "as if with" malloc
*/
/*
* How many slots in the page directory
*/
static unsigned malloc_ninfo;
/*
* Free pages line up here
*/
/*
* 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
*/
#ifdef SANITY
void
{
int j;
/* print out all the pages */
for(j=0;j<=last_index;j++) {
if (pd[j] == MALLOC_NOT_MINE) {
;
j--;
} else if (pd[j] == MALLOC_FREE) {
;
j--;
} else if (pd[j] == MALLOC_FIRST) {
;
j--;
} else if (pd[j] < MALLOC_MAGIC) {
} else {
}
}
break;
}
}
/* print out various info */
}
{
char *q = "malloc() error: ";
suicide = 1;
PR_Abort();
}
{
char *q = "malloc() warning: ";
}
#endif /* SANITY */
/*
* Allocate a number of pages from the OS
*/
static caddr_t
{
malloc_brk = tail;
return result;
}
#ifdef EXTRA_SANITY
wrterror("map_pages fails\n");
#endif
return 0;
}
/*
* Extend page directory
*/
static int
{
int i;
/* Make it this many pages */
i /= malloc_pagesize;
i += 2;
/* Get new pages, if you used this much mem you don't care :-) */
if (!young)
return 0;
/* Copy the old stuff */
malloc_ninfo * sizeof *page_dir);
/* register the new size */
/* swap the pointers */
/* Mark the pages */
while (--i) {
}
/* Now free the old stuff */
return 1;
}
/*
* Set entry in page directory.
* Extend page directory if need be.
*/
static int
{
return 0;
return 1;
}
/*
* Initialize the world
*/
static void
malloc_init (void)
{
int i;
char *p;
#ifdef DEBUG
for (p=getenv("MALLOC_OPTIONS"); p && *p; p++) {
switch (*p) {
case 'a': malloc_abort = 0; break;
case 'd': malloc_stats = 0; break;
case 'r': malloc_realloc = 0; break;
default:
wrtwarning("Unknown chars in MALLOC_OPTIONS\n");
break;
}
}
#endif
#ifndef malloc_pagesize
/* determine our pagesize */
#endif /* malloc_pagesize */
#ifndef malloc_pageshift
/* determine how much we shift by to get there */
#endif /* malloc_pageshift */
#ifndef malloc_cache
#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);
break;
i += i;
}
malloc_minsize = i;
#endif /* malloc_minsize */
/* Allocate one page for the page directory */
#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;
*/
/* Clear it */
/* Find out how much it tells us */
/* Plug the page directory into itself */
#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
*/
{
void *p,*delay_free = 0;
int i;
/* How many pages ? */
size &= ~malloc_pagemask;
p = 0;
/* Look for free pages before asking for more */
#ifdef EXTRA_SANITY
wrterror("zero entry on free_list\n");
wrterror("sick entry on free_list\n");
}
wrterror("entry on free_list past brk\n");
!= MALLOC_FREE) {
wrterror("non-free first page on free-list\n");
}
!= MALLOC_FREE)
wrterror("non-free last page on free-list\n");
#endif /* EXTRA_SANITY */
continue;
delay_free = pf;
break;
} else {
break;
}
}
#ifdef EXTRA_SANITY
!= MALLOC_FREE) {
wrterror("allocated non-free page on free-list\n");
}
#endif /* EXTRA_SANITY */
/* Map new pages */
if (!p)
if (p) {
/* Mark the pages in the directory */
for (i=1;i<size;i++)
}
if (delay_free) {
if (!px)
else
}
return p;
}
/*
* Allocate a page of fragments
*/
static int
{
void *pp;
int i,k,l;
/* Allocate a new bucket */
if (!pp)
return 0;
l += sizeof(u_long) *
if ((1<<(bits)) <= l+l) {
} else {
}
if (!bp)
return 0;
if (!i)
return 0;
/* We can safely assume that there is nobody in this chain */
/* set all valid bits in the bits */
i = 0;
/*
for(;k-i >= MALLOC_BITS; i += MALLOC_BITS)
bp->bits[i / MALLOC_BITS] = ~0;
*/
for(; i < k; i++)
return 1;
/* We may have used the first ones already */
for(i=0;l > 0;i++) {
l -= (1 << bits);
}
return 1;
}
/*
* Allocate a fragment
*/
{
size_t s;
int j;
int k;
/* Don't bother with anything less than this */
if (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 */
;
/* Find that bit */
k = 0;
while ((bf & 1) == 0) {
bf >>= 1;
k++;
}
}
}
{
void *result;
/* Round up to a multiple of 8 bytes */
if (size & 7) {
}
if (!initialized)
malloc_init();
#ifdef SANITY
if (suicide)
PR_Abort();
#endif
if (size <= malloc_maxsize)
else
#ifdef SANITY
if (malloc_abort && !result)
wrterror("malloc() returns NULL\n");
#endif
return result;
}
{
void *result;
/*
* alignment has to be a power of 2
*/
else
/* Round up to a multiple of 8 bytes */
if (size & 7) {
}
if (!initialized)
malloc_init();
#ifdef SANITY
if (suicide)
abort();
#endif
if (size <= malloc_maxsize)
else
#ifdef SANITY
if (malloc_abort && !result)
wrterror("malloc() returns NULL\n");
#endif
else
return result;
}
{
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 *p;
if (!initialized)
malloc_init();
#ifdef SANITY
if (suicide)
PR_Abort();
#endif
/* used as free() */
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 */
/*
* check if memory was allocated by memalign
*/
tmp_index--;
/*
* memalign-allocated memory
*/
}
/* make sure it makes sense in some fashion */
#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 */
if (*mp == MALLOC_FIRST) {
osize += malloc_pagesize;
mp++;
}
if (!malloc_realloc &&
size > malloc_maxsize &&
return ptr;
}
} else if (*mp >= MALLOC_MAGIC) {
if (!malloc_realloc &&
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 */
else
}
#ifdef DEBUG
else if (malloc_abort)
wrterror("realloc() returns NULL\n");
#endif
return p;
}
/*
* Free a sequence of pages
*/
static void
{
int i;
u_long l;
char *tail;
/* Is it free already ? */
if (info == MALLOC_FREE) {
#ifdef SANITY
#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 ? */
wrterror("freeing messed up page pointer.\n");
#endif
/* Count how many pages it is anyway */
l = i << malloc_pageshift;
/* add to free-list */
if (!px)
/* XXX check success */
px = 0;
} else {
;
/* append to entry */
}
/* prepend to entry */
px = 0;
px = 0;
} else {
continue;
}
break;
}
}
malloc_brk == (void*)sbrk(0)) {
/* Find the page directory entry for the page in question */
/* Now update the directory */
for(i=index;i <= last_index;)
page_dir[i++] = MALLOC_NOT_MINE;
}
}
/*
* Free a chunk, and possibly the page it's on, if the page becomes empty.
*/
static void
{
int i;
void *vp;
/* Make sure that pointer is multiplum of chunk-size */
#ifdef SANITY
wrterror("freeing messed up chunk pointer\n");
#endif
/* Find the chunk number on the page */
/* See if it's free already */
#ifdef SANITY
#endif
return;
}
/* Mark it free */
/* If the page was full before, we need to put it on the queue now */
return;
}
/* If this page isn't empty, don't do anything. */
return;
/* We may want to keep at least one page of each size chunks around. */
return;
/* Find & remove this page in the queue */
#ifdef EXTRA_SANITY
if (!*mp) {
wrterror("Not on queue\n");
}
#endif
}
/* Free the page & the info structure if need be */
} else {
}
}
{
/* 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 */
/*
* check if memory was allocated by memalign
*/
tmp_index--;
/*
* memalign-allocated memory
*/
}
/* make sure it makes sense in some fashion */
if (index < malloc_pageshift) {
#ifdef SANITY
#endif
return;
}
if (index > last_index) {
#ifdef SANITY
#endif
return;
}
/* handle as page-allocation or chunk allocation */
if (info < MALLOC_MAGIC)
else
return;
}
#endif /* _PR_OVERRIDE_MALLOC */