/*
* Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
* Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
* Copyright (c) 1996 by Silicon Graphics. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
*
* Permission is hereby granted to use or copy this program
* for any purpose, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
/*
* These are extra allocation routines which are likely to be less
* frequently used than those in malloc.c. They are separate in the
* hope that the .o file will be excluded from statically linked
* executables. We should probably break this up further.
*/
#include <stdio.h>
#include "gc_priv.h"
extern ptr_t GC_clear_stack(); /* in misc.c, behaves like identity */
void GC_extend_size_map(); /* in misc.c. */
GC_bool GC_alloc_reclaim_list(); /* in malloc.c */
/* Some externally visible but unadvertised variables to allow access to */
/* free lists from inlined allocators without including gc_priv.h */
/* or introducing dependencies on internal data structure layouts. */
ptr_t * CONST GC_objfreelist_ptr = GC_objfreelist;
ptr_t * CONST GC_aobjfreelist_ptr = GC_aobjfreelist;
ptr_t * CONST GC_uobjfreelist_ptr = GC_uobjfreelist;
# ifdef ATOMIC_UNCOLLECTABLE
ptr_t * CONST GC_auobjfreelist_ptr = GC_auobjfreelist;
# endif
/* Allocate a composite object of size n bytes. The caller guarantees */
/* that pointers past the first page are not relevant. Caller holds */
/* allocation lock. */
ptr_t GC_generic_malloc_inner_ignore_off_page(lb, k)
register size_t lb;
register int k;
{
register struct hblk * h;
register word n_blocks;
register word lw;
register ptr_t op;
if (lb <= HBLKSIZE)
return(GC_generic_malloc_inner((word)lb, k));
n_blocks = divHBLKSZ(ADD_SLOP(lb) + HDR_BYTES + HBLKSIZE-1);
if (!GC_is_initialized) GC_init_inner();
/* Do our share of marking work */
if(GC_incremental && !GC_dont_gc)
GC_collect_a_little_inner((int)n_blocks);
lw = ROUNDED_UP_WORDS(lb);
h = GC_allochblk(lw, k, IGNORE_OFF_PAGE);
# ifdef USE_MUNMAP
if (0 == h) {
GC_merge_unmapped();
h = GC_allochblk(lw, k, IGNORE_OFF_PAGE);
}
# endif
while (0 == h && GC_collect_or_expand(n_blocks, TRUE)) {
h = GC_allochblk(lw, k, IGNORE_OFF_PAGE);
}
if (h == 0) {
op = 0;
} else {
op = (ptr_t) (h -> hb_body);
GC_words_wasted += BYTES_TO_WORDS(n_blocks * HBLKSIZE) - lw;
}
GC_words_allocd += lw;
return((ptr_t)op);
}
ptr_t GC_generic_malloc_ignore_off_page(lb, k)
register size_t lb;
register int k;
{
register ptr_t result;
DCL_LOCK_STATE;
GC_INVOKE_FINALIZERS();
DISABLE_SIGNALS();
LOCK();
result = GC_generic_malloc_inner_ignore_off_page(lb,k);
UNLOCK();
ENABLE_SIGNALS();
if (0 == result) {
return((*GC_oom_fn)(lb));
} else {
return(result);
}
}
# if defined(__STDC__) || defined(__cplusplus)
void * GC_malloc_ignore_off_page(size_t lb)
# else
char * GC_malloc_ignore_off_page(lb)
register size_t lb;
# endif
{
return((GC_PTR)GC_generic_malloc_ignore_off_page(lb, NORMAL));
}
# if defined(__STDC__) || defined(__cplusplus)
void * GC_malloc_atomic_ignore_off_page(size_t lb)
# else
char * GC_malloc_atomic_ignore_off_page(lb)
register size_t lb;
# endif
{
return((GC_PTR)GC_generic_malloc_ignore_off_page(lb, PTRFREE));
}
/* Increment GC_words_allocd from code that doesn't have direct access */
/* to GC_arrays. */
# ifdef __STDC__
void GC_incr_words_allocd(size_t n)
{
GC_words_allocd += n;
}
/* The same for GC_mem_freed. */
void GC_incr_mem_freed(size_t n)
{
GC_mem_freed += n;
}
# endif /* __STDC__ */
/* Analogous to the above, but assumes a small object size, and */
/* bypasses MERGE_SIZES mechanism. Used by gc_inline.h. */
#ifdef __STDC__
ptr_t GC_generic_malloc_words_small(size_t lw, int k)
#else
ptr_t GC_generic_malloc_words_small(lw, k)
register word lw;
register int k;
#endif
{
register ptr_t op;
register ptr_t *opp;
register struct obj_kind * kind = GC_obj_kinds + k;
DCL_LOCK_STATE;
GC_INVOKE_FINALIZERS();
DISABLE_SIGNALS();
LOCK();
opp = &(kind -> ok_freelist[lw]);
if( (op = *opp) == 0 ) {
if (!GC_is_initialized) {
GC_init_inner();
}
if (kind -> ok_reclaim_list != 0 || GC_alloc_reclaim_list(kind)) {
op = GC_clear_stack(GC_allocobj((word)lw, k));
}
if (op == 0) {
UNLOCK();
ENABLE_SIGNALS();
return ((*GC_oom_fn)(WORDS_TO_BYTES(lw)));
}
}
*opp = obj_link(op);
obj_link(op) = 0;
GC_words_allocd += lw;
UNLOCK();
ENABLE_SIGNALS();
return((ptr_t)op);
}
#if defined(THREADS) && !defined(SRC_M3)
/* Return a list of 1 or more objects of the indicated size, linked */
/* through the first word in the object. This has the advantage that */
/* it acquires the allocation lock only once, and may greatly reduce */
/* time wasted contending for the allocation lock. Typical usage would */
/* be in a thread that requires many items of the same size. It would */
/* keep its own free list in thread-local storage, and call */
/* GC_malloc_many or friends to replenish it. (We do not round up */
/* object sizes, since a call indicates the intention to consume many */
/* objects of exactly this size.) */
/* Note that the client should usually clear the link field. */
ptr_t GC_generic_malloc_many(lb, k)
register word lb;
register int k;
{
ptr_t op;
register ptr_t p;
ptr_t *opp;
word lw;
register word my_words_allocd;
DCL_LOCK_STATE;
if (!SMALL_OBJ(lb)) {
op = GC_generic_malloc(lb, k);
if(0 != op) obj_link(op) = 0;
return(op);
}
lw = ALIGNED_WORDS(lb);
GC_INVOKE_FINALIZERS();
DISABLE_SIGNALS();
LOCK();
opp = &(GC_obj_kinds[k].ok_freelist[lw]);
if( (op = *opp) == 0 ) {
if (!GC_is_initialized) {
GC_init_inner();
}
op = GC_clear_stack(GC_allocobj(lw, k));
if (op == 0) {
UNLOCK();
ENABLE_SIGNALS();
op = (*GC_oom_fn)(lb);
if(0 != op) obj_link(op) = 0;
return(op);
}
}
*opp = 0;
my_words_allocd = 0;
for (p = op; p != 0; p = obj_link(p)) {
my_words_allocd += lw;
if (my_words_allocd >= BODY_SZ) {
*opp = obj_link(p);
obj_link(p) = 0;
break;
}
}
GC_words_allocd += my_words_allocd;
out:
UNLOCK();
ENABLE_SIGNALS();
return(op);
}
void * GC_malloc_many(size_t lb)
{
return(GC_generic_malloc_many(lb, NORMAL));
}
/* Note that the "atomic" version of this would be unsafe, since the */
/* links would not be seen by the collector. */
# endif
/* Allocate lb bytes of pointerful, traced, but not collectable data */
# ifdef __STDC__
GC_PTR GC_malloc_uncollectable(size_t lb)
# else
GC_PTR GC_malloc_uncollectable(lb)
size_t lb;
# endif
{
register ptr_t op;
register ptr_t *opp;
register word lw;
DCL_LOCK_STATE;
if( SMALL_OBJ(lb) ) {
# ifdef MERGE_SIZES
# ifdef ADD_BYTE_AT_END
if (lb != 0) lb--;
/* We don't need the extra byte, since this won't be */
/* collected anyway. */
# endif
lw = GC_size_map[lb];
# else
lw = ALIGNED_WORDS(lb);
# endif
opp = &(GC_uobjfreelist[lw]);
FASTLOCK();
if( FASTLOCK_SUCCEEDED() && (op = *opp) != 0 ) {
/* See above comment on signals. */
*opp = obj_link(op);
obj_link(op) = 0;
GC_words_allocd += lw;
/* Mark bit ws already set on free list. It will be */
/* cleared only temporarily during a collection, as a */
/* result of the normal free list mark bit clearing. */
GC_non_gc_bytes += WORDS_TO_BYTES(lw);
FASTUNLOCK();
return((GC_PTR) op);
}
FASTUNLOCK();
op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE);
} else {
op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE);
}
if (0 == op) return(0);
/* We don't need the lock here, since we have an undisguised */
/* pointer. We do need to hold the lock while we adjust */
/* mark bits. */
{
register struct hblk * h;
h = HBLKPTR(op);
lw = HDR(h) -> hb_sz;
DISABLE_SIGNALS();
LOCK();
GC_set_mark_bit(op);
GC_non_gc_bytes += WORDS_TO_BYTES(lw);
UNLOCK();
ENABLE_SIGNALS();
return((GC_PTR) op);
}
}
# ifdef ATOMIC_UNCOLLECTABLE
/* Allocate lb bytes of pointerfree, untraced, uncollectable data */
/* This is normally roughly equivalent to the system malloc. */
/* But it may be useful if malloc is redefined. */
# ifdef __STDC__
GC_PTR GC_malloc_atomic_uncollectable(size_t lb)
# else
GC_PTR GC_malloc_atomic_uncollectable(lb)
size_t lb;
# endif
{
register ptr_t op;
register ptr_t *opp;
register word lw;
DCL_LOCK_STATE;
if( SMALL_OBJ(lb) ) {
# ifdef MERGE_SIZES
# ifdef ADD_BYTE_AT_END
if (lb != 0) lb--;
/* We don't need the extra byte, since this won't be */
/* collected anyway. */
# endif
lw = GC_size_map[lb];
# else
lw = ALIGNED_WORDS(lb);
# endif
opp = &(GC_auobjfreelist[lw]);
FASTLOCK();
if( FASTLOCK_SUCCEEDED() && (op = *opp) != 0 ) {
/* See above comment on signals. */
*opp = obj_link(op);
obj_link(op) = 0;
GC_words_allocd += lw;
/* Mark bit was already set while object was on free list. */
GC_non_gc_bytes += WORDS_TO_BYTES(lw);
FASTUNLOCK();
return((GC_PTR) op);
}
FASTUNLOCK();
op = (ptr_t)GC_generic_malloc((word)lb, AUNCOLLECTABLE);
} else {
op = (ptr_t)GC_generic_malloc((word)lb, AUNCOLLECTABLE);
}
if (0 == op) return(0);
/* We don't need the lock here, since we have an undisguised */
/* pointer. We do need to hold the lock while we adjust */
/* mark bits. */
{
register struct hblk * h;
h = HBLKPTR(op);
lw = HDR(h) -> hb_sz;
DISABLE_SIGNALS();
LOCK();
GC_set_mark_bit(op);
GC_non_gc_bytes += WORDS_TO_BYTES(lw);
UNLOCK();
ENABLE_SIGNALS();
return((GC_PTR) op);
}
}
#endif /* ATOMIC_UNCOLLECTABLE */