/* alloc.c -- memory management routines
*
* This file deals with memory allocated by the runtime system or *implicitly*
* by MPD programs. Explicit MPD new() and free() calls are supported in
* misc.c.
*/
#include "rts.h"
static Memh all_mem; /* header blocks for MPD allocated memory */
static Mutex mem_mutex; /* protection for all_mem; acquired after res->rmutex.*/
static void do_free ();
/*
* Initialize memory management.
*/
void
mpd_init_mem ()
{
INIT_LOCK (mem_mutex, "mem_mutex");
all_mem = NULL;
}
/*
* Allocate a chunk of contiguous memory and remember who owns it.
*
* If size >= 0, the memory is associated with the current resource.
* If size < 0, the memory is not associated with any resource.
* Need_mutex should be 0 iff the caller already has res->rmutex.
*
* This grabs first the res rmutex first and then mem_mutex. It should
* be possible to do this serially; only the insert into all_mem needs
* mem_mutex, and only the insert into res->meml needs res->rmutex.
*
* The only other place this nesting happens is in mpd_free below, so
* if they are serialized here then they should be serialized there.
* Note that if they are nested, we have to grab rmutex first, since
* sometimes routines in this file are called while rmutex is already
* locked by the caller.
*
* Note that this sometimes grabs rmutex. All such callers (and their
* RTS callers) of this routine that do so do *not* ever hold any
* other lock while calling this, and they must not in future RTS
* mods to preserve the linear ordering.
*/
Ptr
mpd_alc (size, need_rmutex)
int size, need_rmutex;
{
Memh mp;
Rinst res;
if (size >= 0) {
res = CUR_RES;
} else {
res = NULL;
size = -size;
}
if (res != NULL && need_rmutex)
LOCK (res->rmutex, "mpd_alc");
LOCK (mem_mutex, "mpd_alc");
mp = (Memh) mpd_alloc (size + MEMH_SZ);
mp->res = res;
insert (mp, all_mem, mnext, mlast);
UNLOCK (mem_mutex, "mpd_alc");
if (res != NULL) {
insert (mp, res->meml, rnext, rlast);
if (need_rmutex)
UNLOCK (res->rmutex, "mpd_alc");
}
DEBUG (D_ALLOC, "mpd_alc @%06lX (%ld)", mp, size, 0);
return (Ptr) mp + MEMH_SZ;
}
/*
* Called by the generated code to allocate and initialize a string
*/
String *
mpd_alc_string (maxlength)
int maxlength;
{
String *s;
int size = maxlength + STRING_OVH;
mpd_check_stk (CUR_STACK);
s = (String *) mpd_alc (size, 1);
s->size = size;
s->length = -1;
return s;
}
/*
* malloc a chunk of memory and check for success.
*/
Ptr
mpd_alloc (size)
int size;
{
Ptr mp;
mp = MALLOC ((unsigned int) size);
DEBUG (D_ALLOC, "mpd_alloc @%06lX (%ld)", mp, size, 0);
if (mp == NULL)
mpd_abort ("out of memory");
#ifndef NDEBUG
memset (mp, 0xCF, size); /* init to unexpected value to catch errors */
#endif
return mp;
}
/*
* Free memory allocated by mpd_alc or mpd_alc_string.
*/
void
mpd_free (addr)
Ptr addr;
{
mpd_check_stk (CUR_STACK);
do_free (addr, 1);
}
/*
* Free mpd_alc'd memory while holding res->rmutex.
*/
void
mpd_locked_free (addr)
Ptr addr;
{
mpd_check_stk (CUR_STACK);
do_free (addr, 0);
}
/*
* Free a previously allocated chunk of memory.
* Remove record from global and resource memory lists.
*/
static void
do_free (addr, need_res_mutex)
Ptr addr;
int need_res_mutex;
{
Memh mp;
mp = (Memh) (addr - MEMH_SZ);
if ((mp->res != NULL) && need_res_mutex)
LOCK (mp->res->rmutex, "mpd_free");
LOCK (mem_mutex, "mpd_free");
delete (mp, all_mem, mnext, mlast);
if (mp->res != NULL)
delete (mp, mp->res->meml, rnext, rlast);
if ((mp->res != NULL) && need_res_mutex)
UNLOCK (mp->res->rmutex, "mpd_free");
UNLOCK (mem_mutex, "mpd_free");
DEBUG (D_ALLOC, "mpd_free @%06lX", mp, 0, 0);
if (!DBFLAGS (D_NOFREE)) /* can be inhibited using debug flag */
UNMALLOC ((Ptr) mp);
}
/*
* Free all memory belonging to the specified resource.
* The caller must possess res->rmutex.
*/
void
mpd_res_free (res)
Rinst res;
{
Memh mp, nxt;
LOCK (mem_mutex, "mpd_res_free");
for (mp = res->meml; mp; mp = nxt) {
nxt = mp->rnext;
delete (mp, all_mem, mnext, mlast);
DEBUG (D_ALLOC, "mpd_res_free (%06lX)", mp, 0, 0);
UNMALLOC ((Ptr) mp);
}
UNLOCK (mem_mutex, "mpd_res_free");
}
syntax highlighted by Code2HTML, v. 0.9.1