/* malloc.c - dynamic memory allocation for bash. */
-/* Copyright (C) 1985, 1987, 1997 Free Software Foundation, Inc.
+/* Copyright (C) 1985-2005 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ This file is part of GNU Bash, the Bourne-Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-In other words, you are welcome to use, share and improve this program.
-You are forbidden to forbid anyone else to use, share and improve
-what you give them. Help stamp out software-hoarding! */
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
/*
* @(#)nmalloc.c 1 (Caltech) 2/21/82
#define ASSERT(p) \
do \
{ \
- if (!(p)) xbotch((PTR_T)0, ERR_ASSERT_FAILED, __STRING(p), file, line); \
+ if (!(p)) xbotch((PTR_T)0, ERR_ASSERT_FAILED, CPP_STRING(p), file, line); \
} \
while (0)
static union mhead *nextf[NBUCKETS];
-/* busy[i] is nonzero while allocation of block size i is in progress. */
+/* busy[i] is nonzero while allocation or free of block size i is in progress. */
static char busy[NBUCKETS];
static char *memtop; /* top of heap */
-static unsigned long binsizes[NBUCKETS] = {
+static const unsigned long binsizes[NBUCKETS] = {
8UL, 16UL, 32UL, 64UL, 128UL, 256UL, 512UL, 1024UL, 2048UL, 4096UL,
8192UL, 16384UL, 32768UL, 65536UL, 131072UL, 262144UL, 524288UL,
1048576UL, 2097152UL, 4194304UL, 8388608UL, 16777216UL, 33554432UL,
67108864UL, 134217728UL, 268435456UL, 536870912UL, 1073741824UL,
- 2147483648UL, 4294967296UL-1
+ 2147483648UL, 4294967295UL
};
/* binsizes[x] == (1 << ((x) + 3)) */
static PTR_T internal_malloc __P((size_t, const char *, int, int));
static PTR_T internal_realloc __P((PTR_T, size_t, const char *, int, int));
static void internal_free __P((PTR_T, const char *, int, int));
-static PTR_T internal_memalign __P((unsigned int, size_t, const char *, int, int));
+static PTR_T internal_memalign __P((size_t, size_t, const char *, int, int));
#ifndef NO_CALLOC
static PTR_T internal_calloc __P((size_t, size_t, const char *, int, int));
static void internal_cfree __P((PTR_T, const char *, int, int));
#endif /* !HAVE_DECL_SBRK */
#ifdef SHELL
-extern int interrupt_immediately;
+extern int interrupt_immediately, running_trap;
extern int signal_is_trapped __P((int));
#endif
#if !defined (botch)
static void
botch (s, file, line)
+ const char *s;
+ const char *file;
+ int line;
{
- fprintf (stderr, "malloc: failed assertion: %s\n", s);
+ fprintf (stderr, _("malloc: failed assertion: %s\n"), s);
(void)fflush (stderr);
abort ();
}
const char *file;
int line;
{
- fprintf (stderr, "\r\nmalloc: %s:%d: assertion botched\r\n",
- file ? file : "unknown", line);
+ fprintf (stderr, _("\r\nmalloc: %s:%d: assertion botched\r\n"),
+ file ? file : _("unknown"), line);
#ifdef MALLOC_REGISTER
if (mem != NULL && malloc_register)
mregister_describe_mem (mem, stderr);
/* Coalesce two adjacent free blocks off the free list for size NU - 1,
as long as we can find two adjacent free blocks. nextf[NU -1] is
- assumed to not be busy; the caller (morecore()) checks for this. */
+ assumed to not be busy; the caller (morecore()) checks for this.
+ BUSY[NU] must be set to 1. */
static void
bcoalesce (nu)
register int nu;
unsigned long siz;
nbuck = nu - 1;
- if (nextf[nbuck] == 0)
+ if (nextf[nbuck] == 0 || busy[nbuck])
return;
+ busy[nbuck] = 1;
siz = binsize (nbuck);
mp2 = mp1 = nextf[nbuck];
mp1 = mp;
mp = CHAIN (mp);
}
+
if (mp == 0)
- return;
+ {
+ busy[nbuck] = 0;
+ return;
+ }
/* OK, now we have mp1 pointing to the block we want to add to nextf[NU].
CHAIN(mp2) must equal mp1. Check that mp1 and mp are adjacent. */
if (mp2 != mp1 && CHAIN(mp2) != mp1)
- xbotch ((PTR_T)0, 0, "bcoalesce: CHAIN(mp2) != mp1", (char *)NULL, 0);
+ {
+ busy[nbuck] = 0;
+ xbotch ((PTR_T)0, 0, "bcoalesce: CHAIN(mp2) != mp1", (char *)NULL, 0);
+ }
#ifdef MALLOC_DEBUG
if (CHAIN (mp1) != (union mhead *)((char *)mp1 + siz))
- return; /* not adjacent */
-#endif
-
-#ifdef MALLOC_STATS
- _mstats.tbcoalesce++;
- _mstats.ncoalesce[nbuck]++;
+ {
+ busy[nbuck] = 0;
+ return; /* not adjacent */
+ }
#endif
/* Since they are adjacent, remove them from the free list */
nextf[nbuck] = CHAIN (mp);
else
CHAIN (mp2) = CHAIN (mp);
+ busy[nbuck] = 0;
+
+#ifdef MALLOC_STATS
+ _mstats.tbcoalesce++;
+ _mstats.ncoalesce[nbuck]++;
+#endif
/* And add the combined two blocks to nextf[NU]. */
mp1->mh_alloc = ISFREE;
/* Split a block at index > NU (but less than SPLIT_MAX) into a set of
blocks of the correct size, and attach them to nextf[NU]. nextf[NU]
is assumed to be empty. Must be called with signals blocked (e.g.,
- by morecore()). */
+ by morecore()). BUSY[NU] must be set to 1. */
static void
bsplit (nu)
register int nu;
/* XXX might want to split only if nextf[nbuck] has >= 2 blocks free
and nbuck is below some threshold. */
+ /* Remove the block from the chain of larger blocks. */
+ busy[nbuck] = 1;
+ mp = nextf[nbuck];
+ nextf[nbuck] = CHAIN (mp);
+ busy[nbuck] = 0;
+
#ifdef MALLOC_STATS
_mstats.tbsplit++;
_mstats.nsplit[nbuck]++;
siz = binsize (nu);
nblks = binsize (nbuck) / siz;
- /* Remove the block from the chain of larger blocks. */
- mp = nextf[nbuck];
- nextf[nbuck] = CHAIN (mp);
-
/* Split the block and put it on the requested chain. */
nextf[nu] = mp;
while (1)
CHAIN (mp) = 0;
}
+/* Take the memory block MP and add it to a chain < NU. NU is the right bucket,
+ but is busy. This avoids memory orphaning. */
static void
-block_signals (setp, osetp)
+xsplit (mp, nu)
+ union mhead *mp;
+ int nu;
+{
+ union mhead *nh;
+ int nbuck, nblks, split_max;
+ unsigned long siz;
+
+ nbuck = nu - 1;
+ while (nbuck >= SPLIT_MIN && busy[nbuck])
+ nbuck--;
+ if (nbuck < SPLIT_MIN)
+ return;
+
+#ifdef MALLOC_STATS
+ _mstats.tbsplit++;
+ _mstats.nsplit[nu]++;
+#endif
+
+ /* Figure out how many blocks we'll get. */
+ siz = binsize (nu); /* original block size */
+ nblks = siz / binsize (nbuck); /* should be 2 most of the time */
+
+ /* And add it to nextf[nbuck] */
+ siz = binsize (nbuck); /* XXX - resetting here */
+ nh = mp;
+ while (1)
+ {
+ mp->mh_alloc = ISFREE;
+ mp->mh_index = nbuck;
+ if (--nblks <= 0) break;
+ CHAIN (mp) = (union mhead *)((char *)mp + siz);
+ mp = (union mhead *)((char *)mp + siz);
+ }
+ busy[nbuck] = 1;
+ CHAIN (mp) = nextf[nbuck];
+ nextf[nbuck] = nh;
+ busy[nbuck] = 0;
+}
+
+void
+_malloc_block_signals (setp, osetp)
sigset_t *setp, *osetp;
{
#ifdef HAVE_POSIX_SIGNALS
#endif
}
-static void
-unblock_signals (setp, osetp)
+void
+_malloc_unblock_signals (setp, osetp)
sigset_t *setp, *osetp;
{
#ifdef HAVE_POSIX_SIGNALS
_mstats.nlesscore[nu]++;
#endif
}
-
+
+/* Ask system for more memory; add to NEXTF[NU]. BUSY[NU] must be set to 1. */
static void
-morecore (nu) /* ask system for more memory */
+morecore (nu)
register int nu; /* size index to get more of */
{
register union mhead *mp;
/* Block all signals in case we are executed from a signal handler. */
blocked_sigs = 0;
#ifdef SHELL
- if (interrupt_immediately || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD))
+# if defined (SIGCHLD)
+ if (interrupt_immediately || running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD))
+# else
+ if (interrupt_immediately || running_trap || signal_is_trapped (SIGINT))
+# endif
#endif
{
- block_signals (&set, &oset);
+ _malloc_block_signals (&set, &oset);
blocked_sigs = 1;
}
}
/* Try to coalesce two adjacent blocks from the free list on nextf[nu - 1],
- if we can, and we're withing the range of the block coalescing limits. */
+ if we can, and we're within the range of the block coalescing limits. */
if (nu >= COMBINE_MIN && nu < COMBINE_MAX && busy[nu - 1] == 0 && nextf[nu - 1])
{
bcoalesce (nu);
morecore_done:
if (blocked_sigs)
- unblock_signals (&set, &oset);
+ _malloc_unblock_signals (&set, &oset);
}
static void
/* If not for this check, we would gobble a clobbered free chain ptr
and bomb out on the NEXT allocate of this size block */
if (p->mh_alloc != ISFREE || p->mh_index != nunits)
- xbotch ((PTR_T)(p+1), 0, "malloc: block on free list clobbered", file, line);
+ xbotch ((PTR_T)(p+1), 0, _("malloc: block on free list clobbered"), file, line);
/* Fill in the info, and set up the magic numbers for range checking. */
p->mh_alloc = ISALLOC;
{
if (p->mh_alloc == ISFREE)
xbotch (mem, ERR_DUPFREE,
- "free: called with already freed block argument", file, line);
+ _("free: called with already freed block argument"), file, line);
else
xbotch (mem, ERR_UNALLOC,
- "free: called with unallocated block argument", file, line);
+ _("free: called with unallocated block argument"), file, line);
}
ASSERT (p->mh_magic2 == MAGIC2);
if (IN_BUCKET(nbytes, nunits) == 0)
xbotch (mem, ERR_UNDERFLOW,
- "free: underflow detected; mh_nbytes out of range", file, line);
+ _("free: underflow detected; mh_nbytes out of range"), file, line);
ap += p->mh_nbytes;
z = mg.s;
*z++ = *ap++, *z++ = *ap++, *z++ = *ap++, *z++ = *ap++;
if (mg.i != p->mh_nbytes)
- xbotch (mem, ERR_ASSERT_FAILED, "free: start and end chunk sizes differ", file, line);
+ xbotch (mem, ERR_ASSERT_FAILED, _("free: start and end chunk sizes differ"), file, line);
-#if 1
- if (nunits >= LESSCORE_MIN && ((char *)p + binsize(nunits) == memtop))
+#if GLIBC21
+ if (nunits >= LESSCORE_MIN && ((char *)p + binsize(nunits) == sbrk (0)))
#else
- if (((char *)p + binsize(nunits) == memtop) && nunits >= LESSCORE_MIN)
+ if (nunits >= LESSCORE_MIN && ((char *)p + binsize(nunits) == memtop))
#endif
{
/* If above LESSCORE_FRC, give back unconditionally. This should be set
high enough to be infrequently encountered. If between LESSCORE_MIN
- and LESSCORE_FRC, call lesscore if the bucket is marked as busy (in
- which case we would punt below and leak memory) or if there's already
- a block on the free list. */
+ and LESSCORE_FRC, call lesscore if the bucket is marked as busy or if
+ there's already a block on the free list. */
if ((nunits >= LESSCORE_FRC) || busy[nunits] || nextf[nunits] != 0)
{
lesscore (nunits);
#endif
ASSERT (nunits < NBUCKETS);
- p->mh_alloc = ISFREE;
if (busy[nunits] == 1)
- return; /* this is bogus, but at least it won't corrupt the chains */
+ {
+ xsplit (p, nunits); /* split block and add to different chain */
+ goto free_return;
+ }
+ p->mh_alloc = ISFREE;
/* Protect against signal handlers calling malloc. */
busy[nunits] = 1;
/* Put this block on the free list. */
busy[nunits] = 0;
free_return:
+ ; /* Empty statement in case this is the end of the function */
#ifdef MALLOC_STATS
_mstats.nmalloc[nunits]--;
if (p->mh_alloc != ISALLOC)
xbotch (mem, ERR_UNALLOC,
- "realloc: called with unallocated block argument", file, line);
+ _("realloc: called with unallocated block argument"), file, line);
ASSERT (p->mh_magic2 == MAGIC2);
nbytes = ALLOCATED_BYTES(p->mh_nbytes);
original number of bytes requested. */
if (IN_BUCKET(nbytes, nunits) == 0)
xbotch (mem, ERR_UNDERFLOW,
- "realloc: underflow detected; mh_nbytes out of range", file, line);
+ _("realloc: underflow detected; mh_nbytes out of range"), file, line);
m = (char *)mem + (tocopy = p->mh_nbytes);
z = mg.s;
*z++ = *m++, *z++ = *m++, *z++ = *m++, *z++ = *m++;
if (mg.i != p->mh_nbytes)
- xbotch (mem, ERR_ASSERT_FAILED, "realloc: start and end chunk sizes differ", file, line);
+ xbotch (mem, ERR_ASSERT_FAILED, _("realloc: start and end chunk sizes differ"), file, line);
#ifdef MALLOC_WATCH
if (_malloc_nwatch > 0)
static PTR_T
internal_memalign (alignment, size, file, line, flags)
- unsigned int alignment;
+ size_t alignment;
size_t size;
const char *file;
int line, flags;
PTR_T
sh_memalign (alignment, size, file, line)
- unsigned int alignment;
+ size_t alignment;
size_t size;
const char *file;
int line;
PTR_T
memalign (alignment, size)
- unsigned int alignment;
+ size_t alignment;
size_t size;
{
return internal_memalign (alignment, size, (char *)NULL, 0, 0);