mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-18 03:40:47 -08:00
1. Where Type is a pointer type with a signature, replace CHECKL(TypeCheck(val)) with CHECKD(Type, val). 2. Where Type is a pointer type with no signature, replace CHECKL(TypeCheck(val)) with CHECKD_NOSIG(Type, val). 3. Where Type is a pointer type with a signature, but the structure is not visible at point of checking, replace CHECKL(TypeCheck(val)) with CHECKD_NOSIG(Type, val). Reference <design/check/#.hidden-type> 4. Make BTCheck extern and use it where possible. 5. Replace AVER(TypeCheck(val)) with AVERT(Type, val). Copied from Perforce Change: 185263 ServerID: perforce.ravenbrook.com
1970 lines
44 KiB
C
1970 lines
44 KiB
C
/* mpsi.c: MEMORY POOL SYSTEM C INTERFACE LAYER
|
|
*
|
|
* $Id$
|
|
* Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
|
|
* Portions copyright (c) 2002 Global Graphics Software.
|
|
*
|
|
* .purpose: This code bridges between the MPS interface to C,
|
|
* <code/mps.h>, and the internal MPM interfaces, as defined by
|
|
* <code/mpm.h>. .purpose.check: It performs checking of the C client's
|
|
* usage of the MPS Interface. .purpose.thread: It excludes multiple
|
|
* threads from the MPM by locking the Arena (see .thread-safety).
|
|
*
|
|
* .design: <design/interface-c/>
|
|
*
|
|
*
|
|
* NOTES
|
|
*
|
|
* .note.break-out: Take care not to return when "inside" the Arena
|
|
* (between ArenaEnter and ArenaLeave) as this will leave the Arena in
|
|
* an unsuitable state for re-entry.
|
|
*
|
|
*
|
|
* TRANSGRESSIONS (rule.impl.trans)
|
|
*
|
|
* .check.protocol: (rule.impl.req) More could be done in this code to
|
|
* check that protocols are obeyed by the client. It probably doesn't
|
|
* meet checking requirements.
|
|
*
|
|
* .poll: (rule.universal.complete) Various allocation methods call
|
|
* ArenaPoll to allow the MPM to "steal" CPU time and get on with
|
|
* background tasks such as incremental GC.
|
|
*
|
|
* .root-mode: (rule.universal.complete) The root "mode", which
|
|
* specifies things like the protectability of roots, is ignored at
|
|
* present. This is because the MPM doesn't ever try to protect them.
|
|
* In future, it will.
|
|
*
|
|
* .reg-scan: (rule.universal.complete) At present, we only support
|
|
* register scanning using our own ambiguous register and stack scanning
|
|
* method, mps_stack_scan_ambig. This may never change, but the way the
|
|
* interface is designed allows for the possibility of change.
|
|
*
|
|
* .naming: (rule.impl.guide) The exported identifiers do not follow the
|
|
* normal MPS naming conventions. See <design/interface-c/#naming>. */
|
|
|
|
#include "mpm.h"
|
|
#include "mps.h"
|
|
#include "sac.h"
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
|
SRCID(mpsi, "$Id$");
|
|
|
|
|
|
/* mpsi_check -- check consistency of interface mappings
|
|
*
|
|
* .check.purpose: The mpsi_check function attempts to check whether
|
|
* the defintions in <code/mpsi.h> match the equivalent definition in
|
|
* the MPM. It is checking the assumptions made in the other functions
|
|
* in this implementation.
|
|
*
|
|
* .check.empty: Note that mpsi_check compiles away to almost nothing.
|
|
*
|
|
* .check.enum.cast: enum comparisons have to be cast to avoid a warning
|
|
* from the SunPro C compiler. See builder.sc.warn.enum. */
|
|
|
|
static Bool mpsi_check(void)
|
|
{
|
|
CHECKL(COMPATTYPE(mps_res_t, Res));
|
|
|
|
/* Check that external and internal message types match. */
|
|
/* See <code/mps.h#message.types> and */
|
|
/* <code/mpmtypes.h#message.types>. */
|
|
/* Also see .check.enum.cast. */
|
|
CHECKL(COMPATTYPE(mps_message_type_t, MessageType));
|
|
CHECKL((int)MessageTypeFINALIZATION
|
|
== (int)_mps_MESSAGE_TYPE_FINALIZATION);
|
|
CHECKL((int)MessageTypeGC
|
|
== (int)_mps_MESSAGE_TYPE_GC);
|
|
CHECKL((int)MessageTypeGCSTART
|
|
== (int)_mps_MESSAGE_TYPE_GC_START);
|
|
|
|
/* The external idea of a word width and the internal one */
|
|
/* had better match. See <design/interface-c/#cons>. */
|
|
CHECKL(sizeof(mps_word_t) == sizeof(void *));
|
|
CHECKL(COMPATTYPE(mps_word_t, Word));
|
|
|
|
/* The external idea of an address and the internal one */
|
|
/* had better match. */
|
|
CHECKL(COMPATTYPE(mps_addr_t, Addr));
|
|
|
|
/* The external idea of size and the internal one had */
|
|
/* better match. See <design/interface-c/#cons.size> */
|
|
/* and <design/interface-c/#pun.size>. */
|
|
CHECKL(COMPATTYPE(size_t, Size));
|
|
|
|
/* Clock values are passed from external to internal and back */
|
|
/* out to external. */
|
|
CHECKL(COMPATTYPE(mps_clock_t, Clock));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* Ranks
|
|
*
|
|
* Here a rank returning function is defined for all client visible
|
|
* ranks.
|
|
*
|
|
* .rank.final.not: RankFINAL does not have a corresponding function as
|
|
* it is only used internally. */
|
|
|
|
mps_rank_t mps_rank_ambig(void)
|
|
{
|
|
return RankAMBIG;
|
|
}
|
|
|
|
mps_rank_t mps_rank_exact(void)
|
|
{
|
|
return RankEXACT;
|
|
}
|
|
|
|
mps_rank_t mps_rank_weak(void)
|
|
{
|
|
return RankWEAK;
|
|
}
|
|
|
|
|
|
mps_res_t mps_arena_extend(mps_arena_t arena,
|
|
mps_addr_t base, size_t size)
|
|
{
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
AVER(size > 0);
|
|
res = ArenaExtend(arena, (Addr)base, (Size)size);
|
|
ArenaLeave(arena);
|
|
|
|
return (mps_res_t)res;
|
|
}
|
|
|
|
size_t mps_arena_reserved(mps_arena_t arena)
|
|
{
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
size = ArenaReserved(arena);
|
|
ArenaLeave(arena);
|
|
|
|
return (size_t)size;
|
|
}
|
|
|
|
size_t mps_arena_committed(mps_arena_t arena)
|
|
{
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
size = ArenaCommitted(arena);
|
|
ArenaLeave(arena);
|
|
|
|
return (size_t)size;
|
|
}
|
|
|
|
size_t mps_arena_spare_committed(mps_arena_t arena)
|
|
{
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
size = ArenaSpareCommitted(arena);
|
|
ArenaLeave(arena);
|
|
|
|
return (size_t)size;
|
|
}
|
|
|
|
size_t mps_arena_commit_limit(mps_arena_t arena)
|
|
{
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
size = ArenaCommitLimit(arena);
|
|
ArenaLeave(arena);
|
|
|
|
return size;
|
|
}
|
|
|
|
mps_res_t mps_arena_commit_limit_set(mps_arena_t arena, size_t limit)
|
|
{
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
res = ArenaSetCommitLimit(arena, limit);
|
|
ArenaLeave(arena);
|
|
|
|
return res;
|
|
}
|
|
|
|
void mps_arena_spare_commit_limit_set(mps_arena_t arena, size_t limit)
|
|
{
|
|
ArenaEnter(arena);
|
|
ArenaSetSpareCommitLimit(arena, limit);
|
|
ArenaLeave(arena);
|
|
|
|
return;
|
|
}
|
|
|
|
size_t mps_arena_spare_commit_limit(mps_arena_t arena)
|
|
{
|
|
size_t limit;
|
|
|
|
ArenaEnter(arena);
|
|
limit = ArenaSpareCommitLimit(arena);
|
|
ArenaLeave(arena);
|
|
|
|
return limit;
|
|
}
|
|
|
|
void mps_arena_clamp(mps_arena_t arena)
|
|
{
|
|
ArenaEnter(arena);
|
|
ArenaClamp(ArenaGlobals(arena));
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
void mps_arena_release(mps_arena_t arena)
|
|
{
|
|
ArenaEnter(arena);
|
|
ArenaRelease(ArenaGlobals(arena));
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
void mps_arena_park(mps_arena_t arena)
|
|
{
|
|
ArenaEnter(arena);
|
|
ArenaPark(ArenaGlobals(arena));
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
void mps_arena_expose(mps_arena_t arena)
|
|
{
|
|
ArenaEnter(arena);
|
|
ArenaExposeRemember(ArenaGlobals(arena), 0);
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
/* Null implementations of remember and restore */
|
|
void mps_arena_unsafe_expose_remember_protection(mps_arena_t arena)
|
|
{
|
|
ArenaEnter(arena);
|
|
ArenaExposeRemember(ArenaGlobals(arena), 1);
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
void mps_arena_unsafe_restore_protection(mps_arena_t arena)
|
|
{
|
|
ArenaEnter(arena);
|
|
ArenaRestoreProtection(ArenaGlobals(arena));
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
mps_res_t mps_arena_start_collect(mps_arena_t arena)
|
|
{
|
|
Res res;
|
|
ArenaEnter(arena);
|
|
res = ArenaStartCollect(ArenaGlobals(arena), TraceStartWhyCLIENTFULL_INCREMENTAL);
|
|
ArenaLeave(arena);
|
|
return res;
|
|
}
|
|
|
|
mps_res_t mps_arena_collect(mps_arena_t arena)
|
|
{
|
|
Res res;
|
|
ArenaEnter(arena);
|
|
res = ArenaCollect(ArenaGlobals(arena), TraceStartWhyCLIENTFULL_BLOCK);
|
|
ArenaLeave(arena);
|
|
return res;
|
|
}
|
|
|
|
mps_bool_t mps_arena_step(mps_arena_t arena,
|
|
double interval,
|
|
double multiplier)
|
|
{
|
|
Bool b;
|
|
ArenaEnter(arena);
|
|
b = ArenaStep(ArenaGlobals(arena), interval, multiplier);
|
|
ArenaLeave(arena);
|
|
return b;
|
|
}
|
|
|
|
|
|
/* mps_arena_create -- create an arena object */
|
|
|
|
mps_res_t mps_arena_create(mps_arena_t *mps_arena_o,
|
|
mps_arena_class_t mps_arena_class, ...)
|
|
{
|
|
mps_res_t res;
|
|
va_list varargs;
|
|
va_start(varargs, mps_arena_class);
|
|
res = mps_arena_create_v(mps_arena_o, mps_arena_class, varargs);
|
|
va_end(varargs);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* mps_arena_create_v -- create an arena object */
|
|
|
|
mps_res_t mps_arena_create_v(mps_arena_t *mps_arena_o,
|
|
mps_arena_class_t arena_class,
|
|
va_list varargs)
|
|
{
|
|
mps_arg_s args[MPS_ARGS_MAX];
|
|
AVERT(ArenaClass, arena_class);
|
|
arena_class->varargs(args, varargs);
|
|
return mps_arena_create_k(mps_arena_o, arena_class, args);
|
|
}
|
|
|
|
|
|
/* mps_arena_create_k -- create an arena object */
|
|
|
|
mps_res_t mps_arena_create_k(mps_arena_t *mps_arena_o,
|
|
mps_arena_class_t arena_class,
|
|
mps_arg_s mps_args[])
|
|
{
|
|
Arena arena;
|
|
Res res;
|
|
|
|
/* This is the first real call that the client will have to make, */
|
|
/* so check static consistency here. */
|
|
AVER(mpsi_check());
|
|
|
|
AVER(mps_arena_o != NULL);
|
|
|
|
res = ArenaCreate(&arena, arena_class, mps_args);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
ArenaLeave(arena);
|
|
*mps_arena_o = (mps_arena_t)arena;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
/* mps_arena_destroy -- destroy an arena object */
|
|
|
|
void mps_arena_destroy(mps_arena_t arena)
|
|
{
|
|
ArenaEnter(arena);
|
|
ArenaDestroy(arena);
|
|
}
|
|
|
|
|
|
/* mps_arena_has_addr -- is this address managed by this arena? */
|
|
|
|
mps_bool_t mps_arena_has_addr(mps_arena_t arena, mps_addr_t p)
|
|
{
|
|
Bool b;
|
|
|
|
/* One of the few functions that can be called
|
|
during the call to an MPS function. IE this function
|
|
can be called when walking the heap. */
|
|
ArenaEnterRecursive(arena);
|
|
AVERT(Arena, arena);
|
|
b = ArenaHasAddr(arena, (Addr)p);
|
|
ArenaLeaveRecursive(arena);
|
|
return b;
|
|
}
|
|
|
|
|
|
/* mps_addr_pool -- return the pool containing the given address
|
|
*
|
|
* Wrapper for PoolOfAddr. Note: may return an MPS-internal pool.
|
|
*/
|
|
|
|
mps_bool_t mps_addr_pool(mps_pool_t *mps_pool_o,
|
|
mps_arena_t arena,
|
|
mps_addr_t p)
|
|
{
|
|
Bool b;
|
|
Pool pool;
|
|
|
|
AVER(mps_pool_o != NULL);
|
|
/* mps_arena -- will be checked by ArenaEnterRecursive */
|
|
/* p -- cannot be checked */
|
|
|
|
/* One of the few functions that can be called
|
|
during the call to an MPS function. IE this function
|
|
can be called when walking the heap. */
|
|
ArenaEnterRecursive(arena);
|
|
b = PoolOfAddr(&pool, arena, (Addr)p);
|
|
ArenaLeaveRecursive(arena);
|
|
|
|
if(b)
|
|
*mps_pool_o = (mps_pool_t)pool;
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
/* mps_addr_fmt -- what format might this address have?
|
|
*
|
|
* .per-pool: There's no reason why all objects in a pool should have
|
|
* the same format. But currently, MPS internals support at most one
|
|
* format per pool.
|
|
*
|
|
* If the address is in a pool and has a format, returns TRUE and
|
|
* updates *mps_fmt_o to be that format. Otherwise, returns FALSE
|
|
* and does not update *mps_fmt_o.
|
|
*
|
|
* Note: may return an MPS-internal format.
|
|
*/
|
|
mps_bool_t mps_addr_fmt(mps_fmt_t *mps_fmt_o,
|
|
mps_arena_t arena,
|
|
mps_addr_t p)
|
|
{
|
|
Bool b;
|
|
Pool pool;
|
|
Format format = 0;
|
|
|
|
AVER(mps_fmt_o != NULL);
|
|
/* mps_arena -- will be checked by ArenaEnterRecursive */
|
|
/* p -- cannot be checked */
|
|
|
|
/* One of the few functions that can be called
|
|
during the call to an MPS function. IE this function
|
|
can be called when walking the heap. */
|
|
ArenaEnterRecursive(arena);
|
|
/* .per-pool */
|
|
b = PoolOfAddr(&pool, arena, (Addr)p);
|
|
if(b)
|
|
b = PoolFormat(&format, pool);
|
|
ArenaLeaveRecursive(arena);
|
|
|
|
if(b)
|
|
*mps_fmt_o = (mps_fmt_t)format;
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
/* mps_fmt_create_k -- create an object format using keyword arguments */
|
|
|
|
mps_res_t mps_fmt_create_k(mps_fmt_t *mps_fmt_o,
|
|
mps_arena_t arena,
|
|
mps_arg_s args[])
|
|
{
|
|
Format format;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_fmt_o != NULL);
|
|
AVERT(Arena, arena);
|
|
AVERT(ArgList, args);
|
|
|
|
res = FormatCreate(&format, arena, args);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*mps_fmt_o = (mps_fmt_t)format;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
/* mps_fmt_create_A -- create an object format of variant A
|
|
*
|
|
* .fmt.create.A.purpose: This function converts an object format spec
|
|
* of variant "A" into an MPM Format object. See
|
|
* <design/interface-c/#fmt.extend> for justification of the way that
|
|
* the format structure is declared as "mps_fmt_A". */
|
|
|
|
mps_res_t mps_fmt_create_A(mps_fmt_t *mps_fmt_o,
|
|
mps_arena_t arena,
|
|
mps_fmt_A_s *mps_fmt_A)
|
|
{
|
|
Format format;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_fmt_o != NULL);
|
|
AVERT(Arena, arena);
|
|
AVER(mps_fmt_A != NULL);
|
|
|
|
MPS_ARGS_BEGIN(args) {
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, mps_fmt_A->align);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, mps_fmt_A->scan);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_SKIP, mps_fmt_A->skip);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_FWD, mps_fmt_A->fwd);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_ISFWD, mps_fmt_A->isfwd);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_PAD, mps_fmt_A->pad);
|
|
res = FormatCreate(&format, arena, args);
|
|
} MPS_ARGS_END(args);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*mps_fmt_o = (mps_fmt_t)format;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
/* mps_fmt_create_B -- create an object format of variant B */
|
|
|
|
mps_res_t mps_fmt_create_B(mps_fmt_t *mps_fmt_o,
|
|
mps_arena_t arena,
|
|
mps_fmt_B_s *mps_fmt_B)
|
|
{
|
|
Format format;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_fmt_o != NULL);
|
|
AVERT(Arena, arena);
|
|
AVER(mps_fmt_B != NULL);
|
|
|
|
MPS_ARGS_BEGIN(args) {
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, mps_fmt_B->align);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, mps_fmt_B->scan);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_SKIP, mps_fmt_B->skip);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_FWD, mps_fmt_B->fwd);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_ISFWD, mps_fmt_B->isfwd);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_PAD, mps_fmt_B->pad);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_CLASS, mps_fmt_B->mps_class);
|
|
res = FormatCreate(&format, arena, args);
|
|
} MPS_ARGS_END(args);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*mps_fmt_o = (mps_fmt_t)format;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
/* mps_fmt_create_auto_header -- create a format of variant auto_header */
|
|
|
|
mps_res_t mps_fmt_create_auto_header(mps_fmt_t *mps_fmt_o,
|
|
mps_arena_t arena,
|
|
mps_fmt_auto_header_s *mps_fmt)
|
|
{
|
|
Format format;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_fmt_o != NULL);
|
|
AVERT(Arena, arena);
|
|
AVER(mps_fmt != NULL);
|
|
|
|
MPS_ARGS_BEGIN(args) {
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, mps_fmt->align);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_HEADER_SIZE, mps_fmt->mps_headerSize);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, mps_fmt->scan);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_SKIP, mps_fmt->skip);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_FWD, mps_fmt->fwd);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_ISFWD, mps_fmt->isfwd);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_PAD, mps_fmt->pad);
|
|
res = FormatCreate(&format, arena, args);
|
|
} MPS_ARGS_END(args);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*mps_fmt_o = (mps_fmt_t)format;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
/* mps_fmt_create_fixed -- create an object format of variant fixed */
|
|
|
|
mps_res_t mps_fmt_create_fixed(mps_fmt_t *mps_fmt_o,
|
|
mps_arena_t arena,
|
|
mps_fmt_fixed_s *mps_fmt_fixed)
|
|
{
|
|
Format format;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_fmt_o != NULL);
|
|
AVERT(Arena, arena);
|
|
AVER(mps_fmt_fixed != NULL);
|
|
|
|
MPS_ARGS_BEGIN(args) {
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, mps_fmt_fixed->align);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, mps_fmt_fixed->scan);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_FWD, mps_fmt_fixed->fwd);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_ISFWD, mps_fmt_fixed->isfwd);
|
|
MPS_ARGS_ADD(args, MPS_KEY_FMT_PAD, mps_fmt_fixed->pad);
|
|
res = FormatCreate(&format, arena, args);
|
|
} MPS_ARGS_END(args);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*mps_fmt_o = (mps_fmt_t)format;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
/* mps_fmt_destroy -- destroy a format object */
|
|
|
|
void mps_fmt_destroy(mps_fmt_t format)
|
|
{
|
|
Arena arena;
|
|
|
|
AVER(TESTT(Format, format));
|
|
arena = FormatArena(format);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
FormatDestroy(format);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
mps_res_t mps_pool_create(mps_pool_t *mps_pool_o, mps_arena_t arena,
|
|
mps_class_t mps_class, ...)
|
|
{
|
|
mps_res_t res;
|
|
va_list varargs;
|
|
va_start(varargs, mps_class);
|
|
res = mps_pool_create_v(mps_pool_o, arena, mps_class, varargs);
|
|
va_end(varargs);
|
|
return res;
|
|
}
|
|
|
|
mps_res_t mps_pool_create_v(mps_pool_t *mps_pool_o, mps_arena_t arena,
|
|
mps_class_t class, va_list varargs)
|
|
{
|
|
mps_arg_s args[MPS_ARGS_MAX];
|
|
AVERT(PoolClass, class);
|
|
class->varargs(args, varargs);
|
|
return mps_pool_create_k(mps_pool_o, arena, class, args);
|
|
}
|
|
|
|
mps_res_t mps_pool_create_k(mps_pool_t *mps_pool_o, mps_arena_t arena,
|
|
mps_class_t class, mps_arg_s args[])
|
|
{
|
|
Pool pool;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_pool_o != NULL);
|
|
AVERT(Arena, arena);
|
|
AVERT(PoolClass, class);
|
|
AVERT(ArgList, args);
|
|
|
|
res = PoolCreate(&pool, arena, class, args);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*mps_pool_o = (mps_pool_t)pool;
|
|
return res;
|
|
}
|
|
|
|
void mps_pool_destroy(mps_pool_t pool)
|
|
{
|
|
Arena arena;
|
|
|
|
AVER(TESTT(Pool, pool));
|
|
arena = PoolArena(pool);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
PoolDestroy(pool);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
mps_res_t mps_alloc(mps_addr_t *p_o, mps_pool_t pool, size_t size)
|
|
{
|
|
Arena arena;
|
|
Addr p;
|
|
Res res;
|
|
|
|
AVER(TESTT(Pool, pool));
|
|
arena = PoolArena(pool);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
ArenaPoll(ArenaGlobals(arena)); /* .poll */
|
|
|
|
AVER(p_o != NULL);
|
|
AVERT(Pool, pool);
|
|
AVER(size > 0);
|
|
/* Note: class may allow unaligned size, see */
|
|
/* <design/class-interface/#alloc.size.align>. */
|
|
/* Rest ignored, see .varargs. */
|
|
|
|
/* @@@@ There is currently no requirement for reservoirs to work */
|
|
/* with unbuffered allocation. */
|
|
res = PoolAlloc(&p, pool, size, FALSE);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*p_o = (mps_addr_t)p;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
/* mps_alloc_v -- allocate in pool with varargs. Deprecated in 1.112. */
|
|
|
|
mps_res_t mps_alloc_v(mps_addr_t *p_o, mps_pool_t mps_pool, size_t size,
|
|
va_list args)
|
|
{
|
|
mps_res_t res;
|
|
|
|
UNUSED(args); /* See .varargs. */
|
|
res = mps_alloc(p_o, mps_pool, size);
|
|
return res;
|
|
}
|
|
|
|
|
|
void mps_free(mps_pool_t pool, mps_addr_t p, size_t size)
|
|
{
|
|
Arena arena;
|
|
|
|
AVER(TESTT(Pool, pool));
|
|
arena = PoolArena(pool);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVERT(Pool, pool);
|
|
AVER(size > 0);
|
|
/* Note: class may allow unaligned size, see */
|
|
/* <design/class-interface/#alloc.size.align>. */
|
|
|
|
PoolFree(pool, (Addr)p, size);
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* mps_ap_create -- create an allocation point */
|
|
|
|
mps_res_t mps_ap_create(mps_ap_t *mps_ap_o, mps_pool_t pool, ...)
|
|
{
|
|
mps_res_t res;
|
|
va_list varargs;
|
|
va_start(varargs, pool);
|
|
res = mps_ap_create_v(mps_ap_o, pool, varargs);
|
|
va_end(varargs);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* mps_ap_create_v -- create an allocation point, with varargs */
|
|
|
|
mps_res_t mps_ap_create_v(mps_ap_t *mps_ap_o, mps_pool_t pool,
|
|
va_list varargs)
|
|
{
|
|
Arena arena;
|
|
BufferClass bufclass;
|
|
mps_arg_s args[MPS_ARGS_MAX];
|
|
|
|
AVER(mps_ap_o != NULL);
|
|
AVER(TESTT(Pool, pool));
|
|
arena = PoolArena(pool);
|
|
|
|
ArenaEnter(arena);
|
|
AVERT(Pool, pool);
|
|
bufclass = PoolDefaultBufferClass(pool);
|
|
bufclass->varargs(args, varargs);
|
|
ArenaLeave(arena);
|
|
|
|
return mps_ap_create_k(mps_ap_o, pool, args);
|
|
}
|
|
|
|
/* mps_ap_create_k -- create an allocation point, with keyword args */
|
|
|
|
mps_res_t mps_ap_create_k(mps_ap_t *mps_ap_o,
|
|
mps_pool_t pool,
|
|
mps_arg_s args[]) {
|
|
Arena arena;
|
|
Buffer buf;
|
|
BufferClass bufclass;
|
|
Res res;
|
|
|
|
AVER(mps_ap_o != NULL);
|
|
AVER(TESTT(Pool, pool));
|
|
arena = PoolArena(pool);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVERT(Pool, pool);
|
|
|
|
bufclass = PoolDefaultBufferClass(pool);
|
|
res = BufferCreate(&buf, bufclass, pool, TRUE, args);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
*mps_ap_o = BufferAP(buf);
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
void mps_ap_destroy(mps_ap_t mps_ap)
|
|
{
|
|
Buffer buf = BufferOfAP(mps_ap);
|
|
Arena arena;
|
|
|
|
AVER(mps_ap != NULL);
|
|
AVER(TESTT(Buffer, buf));
|
|
arena = BufferArena(buf);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
BufferDestroy(buf);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* mps_reserve -- allocate store in preparation for initialization
|
|
*
|
|
* .reserve.call: mps_reserve does not call BufferReserve, but instead
|
|
* uses the in-line macro from <code/mps.h>. This is so that it calls
|
|
* mps_ap_fill and thence ArenaPoll (.poll). The consistency checks are
|
|
* those which can be done outside the MPM. See also .commit.call. */
|
|
|
|
mps_res_t (mps_reserve)(mps_addr_t *p_o, mps_ap_t mps_ap, size_t size)
|
|
{
|
|
mps_res_t res;
|
|
|
|
AVER(p_o != NULL);
|
|
AVER(mps_ap != NULL);
|
|
AVER(TESTT(Buffer, BufferOfAP(mps_ap)));
|
|
AVER(mps_ap->init == mps_ap->alloc);
|
|
AVER(size > 0);
|
|
|
|
MPS_RESERVE_BLOCK(res, *p_o, mps_ap, size);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
mps_res_t mps_reserve_with_reservoir_permit(mps_addr_t *p_o,
|
|
mps_ap_t mps_ap, size_t size)
|
|
{
|
|
mps_res_t res;
|
|
|
|
AVER(p_o != NULL);
|
|
AVER(size > 0);
|
|
AVER(mps_ap != NULL);
|
|
AVER(TESTT(Buffer, BufferOfAP(mps_ap)));
|
|
AVER(mps_ap->init == mps_ap->alloc);
|
|
|
|
MPS_RESERVE_WITH_RESERVOIR_PERMIT_BLOCK(res, *p_o, mps_ap, size);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
/* mps_commit -- commit initialized object, finishing allocation
|
|
*
|
|
* .commit.call: mps_commit does not call BufferCommit, but instead uses
|
|
* the in-line commit macro from <code/mps.h>. This is so that it calls
|
|
* mps_ap_trip and thence ArenaPoll in future (.poll). The consistency
|
|
* checks here are the ones which can be done outside the MPM. See also
|
|
* .reserve.call. */
|
|
|
|
mps_bool_t (mps_commit)(mps_ap_t mps_ap, mps_addr_t p, size_t size)
|
|
{
|
|
AVER(mps_ap != NULL);
|
|
AVER(TESTT(Buffer, BufferOfAP(mps_ap)));
|
|
AVER(p != NULL);
|
|
AVER(size > 0);
|
|
AVER(p == mps_ap->init);
|
|
AVER((void *)((char *)mps_ap->init + size) == mps_ap->alloc);
|
|
|
|
return mps_commit(mps_ap, p, size);
|
|
}
|
|
|
|
|
|
/* Allocation frame support
|
|
*
|
|
* These are candidates for being inlineable as macros.
|
|
* These functions are easier to maintain, so we'll avoid
|
|
* macros for now. */
|
|
|
|
|
|
/* mps_ap_frame_push -- push a new allocation frame
|
|
*
|
|
* See <design/alloc-frame/#lw-frame.push>. */
|
|
|
|
mps_res_t (mps_ap_frame_push)(mps_frame_t *frame_o, mps_ap_t mps_ap)
|
|
{
|
|
AVER(frame_o != NULL);
|
|
AVER(mps_ap != NULL);
|
|
|
|
/* Fail if between reserve & commit */
|
|
if ((char *)mps_ap->alloc != (char *)mps_ap->init) {
|
|
return MPS_RES_FAIL;
|
|
}
|
|
|
|
if (!mps_ap->_lwpoppending) {
|
|
/* Valid state for a lightweight push */
|
|
*frame_o = (mps_frame_t)mps_ap->init;
|
|
return MPS_RES_OK;
|
|
} else {
|
|
/* Need a heavyweight push */
|
|
Buffer buf = BufferOfAP(mps_ap);
|
|
Arena arena;
|
|
AllocFrame frame;
|
|
Res res;
|
|
|
|
AVER(TESTT(Buffer, buf));
|
|
arena = BufferArena(buf);
|
|
|
|
ArenaEnter(arena);
|
|
AVERT(Buffer, buf);
|
|
|
|
res = BufferFramePush(&frame, buf);
|
|
|
|
if (res == ResOK) {
|
|
*frame_o = (mps_frame_t)frame;
|
|
}
|
|
ArenaLeave(arena);
|
|
return (mps_res_t)res;
|
|
}
|
|
}
|
|
|
|
/* mps_ap_frame_pop -- push a new allocation frame
|
|
*
|
|
* See <design/alloc-frame/#lw-frame.pop>. */
|
|
|
|
mps_res_t (mps_ap_frame_pop)(mps_ap_t mps_ap, mps_frame_t frame)
|
|
{
|
|
AVER(mps_ap != NULL);
|
|
/* Can't check frame because it's an arbitrary value */
|
|
|
|
/* Fail if between reserve & commit */
|
|
if ((char *)mps_ap->alloc != (char *)mps_ap->init) {
|
|
return MPS_RES_FAIL;
|
|
}
|
|
|
|
if (mps_ap->_enabled) {
|
|
/* Valid state for a lightweight pop */
|
|
mps_ap->_frameptr = (mps_addr_t)frame; /* record pending pop */
|
|
mps_ap->_lwpoppending = TRUE;
|
|
mps_ap->limit = (mps_addr_t)0; /* trap the buffer */
|
|
return MPS_RES_OK;
|
|
|
|
} else {
|
|
/* Need a heavyweight pop */
|
|
Buffer buf = BufferOfAP(mps_ap);
|
|
Arena arena;
|
|
Res res;
|
|
|
|
AVER(TESTT(Buffer, buf));
|
|
arena = BufferArena(buf);
|
|
|
|
ArenaEnter(arena);
|
|
AVERT(Buffer, buf);
|
|
|
|
res = BufferFramePop(buf, (AllocFrame)frame);
|
|
|
|
ArenaLeave(arena);
|
|
return (mps_res_t)res;
|
|
}
|
|
}
|
|
|
|
|
|
/* mps_ap_fill -- called by mps_reserve when an AP hasn't enough arena
|
|
*
|
|
* .ap.fill.internal: mps_ap_fill is normally invoked by the
|
|
* mps_reserve macro, but may be "called" directly by the client code
|
|
* if necessary. See <manual/topic/allocation> */
|
|
|
|
mps_res_t mps_ap_fill(mps_addr_t *p_o, mps_ap_t mps_ap, size_t size)
|
|
{
|
|
Buffer buf = BufferOfAP(mps_ap);
|
|
Arena arena;
|
|
Addr p;
|
|
Res res;
|
|
|
|
AVER(mps_ap != NULL);
|
|
AVER(TESTT(Buffer, buf));
|
|
arena = BufferArena(buf);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
ArenaPoll(ArenaGlobals(arena)); /* .poll */
|
|
|
|
AVER(p_o != NULL);
|
|
AVERT(Buffer, buf);
|
|
AVER(size > 0);
|
|
AVER(SizeIsAligned(size, BufferPool(buf)->alignment));
|
|
|
|
res = BufferFill(&p, buf, size, FALSE);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*p_o = (mps_addr_t)p;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
mps_res_t mps_ap_fill_with_reservoir_permit(mps_addr_t *p_o, mps_ap_t mps_ap,
|
|
size_t size)
|
|
{
|
|
Buffer buf = BufferOfAP(mps_ap);
|
|
Arena arena;
|
|
Addr p;
|
|
Res res;
|
|
|
|
AVER(mps_ap != NULL);
|
|
AVER(TESTT(Buffer, buf));
|
|
arena = BufferArena(buf);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
ArenaPoll(ArenaGlobals(arena)); /* .poll */
|
|
|
|
AVER(p_o != NULL);
|
|
AVERT(Buffer, buf);
|
|
AVER(size > 0);
|
|
AVER(SizeIsAligned(size, BufferPool(buf)->alignment));
|
|
|
|
res = BufferFill(&p, buf, size, TRUE);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*p_o = (mps_addr_t)p;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
/* mps_ap_trip -- called by mps_commit when an AP is tripped
|
|
*
|
|
* .ap.trip.internal: mps_ap_trip is normally invoked by the
|
|
* mps_commit macro, but may be "called" directly by the client code
|
|
* if necessary. See <manual/topic/allocation> */
|
|
|
|
mps_bool_t mps_ap_trip(mps_ap_t mps_ap, mps_addr_t p, size_t size)
|
|
{
|
|
Buffer buf = BufferOfAP(mps_ap);
|
|
Arena arena;
|
|
Bool b;
|
|
|
|
AVER(mps_ap != NULL);
|
|
AVER(TESTT(Buffer, buf));
|
|
arena = BufferArena(buf);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVERT(Buffer, buf);
|
|
AVER(size > 0);
|
|
AVER(SizeIsAligned(size, BufferPool(buf)->alignment));
|
|
|
|
b = BufferTrip(buf, (Addr)p, size);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
/* mps_sac_create -- create an SAC object */
|
|
|
|
mps_res_t mps_sac_create(mps_sac_t *mps_sac_o, mps_pool_t pool,
|
|
size_t classes_count, mps_sac_classes_s *classes)
|
|
{
|
|
Arena arena;
|
|
SAC sac;
|
|
Res res;
|
|
|
|
AVER(mps_sac_o != NULL);
|
|
AVER(TESTT(Pool, pool));
|
|
arena = PoolArena(pool);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
res = SACCreate(&sac, pool, (Count)classes_count, classes);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return (mps_res_t)res;
|
|
*mps_sac_o = ExternalSACOfSAC(sac);
|
|
return (mps_res_t)res;
|
|
}
|
|
|
|
|
|
/* mps_sac_destroy -- destroy an SAC object */
|
|
|
|
void mps_sac_destroy(mps_sac_t mps_sac)
|
|
{
|
|
SAC sac = SACOfExternalSAC(mps_sac);
|
|
Arena arena;
|
|
|
|
AVER(TESTT(SAC, sac));
|
|
arena = SACArena(sac);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
SACDestroy(sac);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* mps_sac_flush -- flush an SAC, releasing all memory held in it */
|
|
|
|
void mps_sac_flush(mps_sac_t mps_sac)
|
|
{
|
|
SAC sac = SACOfExternalSAC(mps_sac);
|
|
Arena arena;
|
|
|
|
AVER(TESTT(SAC, sac));
|
|
arena = SACArena(sac);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
SACFlush(sac);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* mps_sac_fill -- alloc an object, and perhaps fill the cache */
|
|
|
|
mps_res_t mps_sac_fill(mps_addr_t *p_o, mps_sac_t mps_sac, size_t size,
|
|
mps_bool_t has_reservoir_permit)
|
|
{
|
|
SAC sac = SACOfExternalSAC(mps_sac);
|
|
Arena arena;
|
|
Addr p = NULL; /* suppress "may be used uninitialized" */
|
|
Res res;
|
|
|
|
AVER(p_o != NULL);
|
|
AVER(TESTT(SAC, sac));
|
|
arena = SACArena(sac);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
res = SACFill(&p, sac, size, (has_reservoir_permit != 0));
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return (mps_res_t)res;
|
|
*p_o = (mps_addr_t)p;
|
|
return (mps_res_t)res;
|
|
}
|
|
|
|
|
|
/* mps_sac_empty -- free an object, and perhaps empty the cache */
|
|
|
|
void mps_sac_empty(mps_sac_t mps_sac, mps_addr_t p, size_t size)
|
|
{
|
|
SAC sac = SACOfExternalSAC(mps_sac);
|
|
Arena arena;
|
|
|
|
AVER(TESTT(SAC, sac));
|
|
arena = SACArena(sac);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
SACEmpty(sac, (Addr)p, (Size)size);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* mps_sac_alloc -- alloc an object, using cached space if possible */
|
|
|
|
mps_res_t mps_sac_alloc(mps_addr_t *p_o, mps_sac_t mps_sac, size_t size,
|
|
mps_bool_t has_reservoir_permit)
|
|
{
|
|
Res res;
|
|
|
|
AVER(p_o != NULL);
|
|
AVER(TESTT(SAC, SACOfExternalSAC(mps_sac)));
|
|
AVER(size > 0);
|
|
|
|
MPS_SAC_ALLOC_FAST(res, *p_o, mps_sac, size, (has_reservoir_permit != 0));
|
|
return res;
|
|
}
|
|
|
|
|
|
/* mps_sac_free -- free an object, to the cache if possible */
|
|
|
|
void mps_sac_free(mps_sac_t mps_sac, mps_addr_t p, size_t size)
|
|
{
|
|
AVER(TESTT(SAC, SACOfExternalSAC(mps_sac)));
|
|
/* Can't check p outside arena lock */
|
|
AVER(size > 0);
|
|
|
|
MPS_SAC_FREE_FAST(mps_sac, p, size);
|
|
}
|
|
|
|
|
|
/* Roots */
|
|
|
|
|
|
mps_res_t mps_root_create(mps_root_t *mps_root_o, mps_arena_t arena,
|
|
mps_rank_t mps_rank, mps_rm_t mps_rm,
|
|
mps_root_scan_t mps_root_scan, void *p, size_t s)
|
|
{
|
|
Rank rank = (Rank)mps_rank;
|
|
Root root;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_root_o != NULL);
|
|
AVER(mps_rm == (mps_rm_t)0);
|
|
|
|
/* See .root-mode. */
|
|
res = RootCreateFun(&root, arena, rank, mps_root_scan, p, s);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*mps_root_o = (mps_root_t)root;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
mps_res_t mps_root_create_table(mps_root_t *mps_root_o, mps_arena_t arena,
|
|
mps_rank_t mps_rank, mps_rm_t mps_rm,
|
|
mps_addr_t *base, size_t size)
|
|
{
|
|
Rank rank = (Rank)mps_rank;
|
|
Root root;
|
|
RootMode mode = (RootMode)mps_rm;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_root_o != NULL);
|
|
AVER(base != NULL);
|
|
AVER(size > 0);
|
|
|
|
/* .root.table-size: size is the length of the array at base, not */
|
|
/* the size in bytes. However, RootCreateTable expects base and */
|
|
/* limit pointers. Be careful. */
|
|
|
|
res = RootCreateTable(&root, arena, rank, mode,
|
|
(Addr *)base, (Addr *)base + size);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*mps_root_o = (mps_root_t)root;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
mps_res_t mps_root_create_table_masked(mps_root_t *mps_root_o,
|
|
mps_arena_t arena,
|
|
mps_rank_t mps_rank, mps_rm_t mps_rm,
|
|
mps_addr_t *base, size_t size,
|
|
mps_word_t mask)
|
|
{
|
|
Rank rank = (Rank)mps_rank;
|
|
Root root;
|
|
RootMode mode = (RootMode)mps_rm;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_root_o != NULL);
|
|
AVER(base != NULL);
|
|
AVER(size > 0);
|
|
/* Can't check anything about mask */
|
|
|
|
/* See .root.table-size. */
|
|
|
|
res = RootCreateTableMasked(&root, arena, rank, mode,
|
|
(Addr *)base, (Addr *)base + size,
|
|
mask);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*mps_root_o = (mps_root_t)root;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
mps_res_t mps_root_create_fmt(mps_root_t *mps_root_o, mps_arena_t arena,
|
|
mps_rank_t mps_rank, mps_rm_t mps_rm,
|
|
mps_fmt_scan_t scan,
|
|
mps_addr_t base, mps_addr_t limit)
|
|
{
|
|
Rank rank = (Rank)mps_rank;
|
|
Root root;
|
|
RootMode mode = (RootMode)mps_rm;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_root_o != NULL);
|
|
|
|
res = RootCreateFmt(&root, arena, rank, mode, scan, (Addr)base, (Addr)limit);
|
|
|
|
ArenaLeave(arena);
|
|
if (res != ResOK) return res;
|
|
*mps_root_o = (mps_root_t)root;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
mps_res_t mps_root_create_reg(mps_root_t *mps_root_o, mps_arena_t arena,
|
|
mps_rank_t mps_rank, mps_rm_t mps_rm,
|
|
mps_thr_t thread, mps_reg_scan_t mps_reg_scan,
|
|
void *reg_scan_p, size_t mps_size)
|
|
{
|
|
Rank rank = (Rank)mps_rank;
|
|
Root root;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_root_o != NULL);
|
|
AVER(mps_reg_scan != NULL);
|
|
AVER(mps_reg_scan == mps_stack_scan_ambig); /* .reg.scan */
|
|
AVER(reg_scan_p != NULL); /* stackBot */
|
|
AVER(rank == mps_rank_ambig());
|
|
AVER(mps_rm == (mps_rm_t)0);
|
|
|
|
/* See .root-mode. */
|
|
res = RootCreateReg(&root, arena, rank, thread,
|
|
mps_reg_scan, reg_scan_p, mps_size);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*mps_root_o = (mps_root_t)root;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
/* mps_stack_scan_ambig -- scan the thread state ambiguously
|
|
*
|
|
* See .reg-scan. */
|
|
|
|
mps_res_t mps_stack_scan_ambig(mps_ss_t mps_ss,
|
|
mps_thr_t thread, void *p, size_t s)
|
|
{
|
|
ScanState ss = PARENT(ScanStateStruct, ss_s, mps_ss);
|
|
UNUSED(s);
|
|
return ThreadScan(ss, thread, p);
|
|
}
|
|
|
|
|
|
void mps_root_destroy(mps_root_t mps_root)
|
|
{
|
|
Root root = (Root)mps_root;
|
|
Arena arena;
|
|
|
|
arena = RootArena(root);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
RootDestroy(root);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
void (mps_tramp)(void **r_o,
|
|
void *(*f)(void *p, size_t s),
|
|
void *p, size_t s)
|
|
{
|
|
AVER(r_o != NULL);
|
|
AVER(FUNCHECK(f));
|
|
/* Can't check p and s as they are interpreted by the client */
|
|
|
|
ProtTramp(r_o, f, p, s);
|
|
}
|
|
|
|
|
|
mps_res_t mps_thread_reg(mps_thr_t *mps_thr_o, mps_arena_t arena)
|
|
{
|
|
Thread thread;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_thr_o != NULL);
|
|
AVERT(Arena, arena);
|
|
|
|
res = ThreadRegister(&thread, arena);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return res;
|
|
*mps_thr_o = (mps_thr_t)thread;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
void mps_thread_dereg(mps_thr_t thread)
|
|
{
|
|
Arena arena;
|
|
|
|
AVER(ThreadCheckSimple(thread));
|
|
arena = ThreadArena(thread);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
ThreadDeregister(thread, arena);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
void mps_ld_reset(mps_ld_t ld, mps_arena_t arena)
|
|
{
|
|
ArenaEnter(arena);
|
|
LDReset(ld, arena);
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* mps_ld_add -- add a reference to a location dependency
|
|
*
|
|
* See <design/interface-c/#lock-free>. */
|
|
|
|
void mps_ld_add(mps_ld_t ld, mps_arena_t arena, mps_addr_t addr)
|
|
{
|
|
LDAdd(ld, arena, (Addr)addr);
|
|
}
|
|
|
|
|
|
/* mps_ld_merge -- merge two location dependencies
|
|
*
|
|
* See <design/interface-c/#lock-free>. */
|
|
|
|
void mps_ld_merge(mps_ld_t ld, mps_arena_t arena,
|
|
mps_ld_t from)
|
|
{
|
|
LDMerge(ld, arena, from);
|
|
}
|
|
|
|
|
|
/* mps_ld_isstale -- check whether a location dependency is "stale"
|
|
*
|
|
* See <design/interface-c/#lock-free>. */
|
|
|
|
mps_bool_t mps_ld_isstale(mps_ld_t ld, mps_arena_t arena,
|
|
mps_addr_t addr)
|
|
{
|
|
Bool b;
|
|
|
|
b = LDIsStale(ld, arena, (Addr)addr);
|
|
|
|
return (mps_bool_t)b;
|
|
}
|
|
|
|
mps_bool_t mps_ld_isstale_any(mps_ld_t ld, mps_arena_t arena)
|
|
{
|
|
Bool b;
|
|
|
|
b = LDIsStaleAny(ld, arena);
|
|
|
|
return (mps_bool_t)b;
|
|
}
|
|
|
|
mps_res_t mps_fix(mps_ss_t mps_ss, mps_addr_t *ref_io)
|
|
{
|
|
mps_res_t res;
|
|
|
|
MPS_SCAN_BEGIN(mps_ss) {
|
|
res = MPS_FIX(mps_ss, ref_io);
|
|
} MPS_SCAN_END(mps_ss);
|
|
|
|
return res;
|
|
}
|
|
|
|
mps_word_t mps_collections(mps_arena_t arena)
|
|
{
|
|
return ArenaEpoch(arena); /* thread safe: see <code/arena.h#epoch.ts> */
|
|
}
|
|
|
|
|
|
/* mps_finalize -- register for finalization */
|
|
|
|
mps_res_t mps_finalize(mps_arena_t arena, mps_addr_t *refref)
|
|
{
|
|
Res res;
|
|
Addr object;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
object = (Addr)ArenaPeek(arena, (Ref *)refref);
|
|
res = ArenaFinalize(arena, object);
|
|
|
|
ArenaLeave(arena);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* mps_definalize -- deregister for finalization */
|
|
|
|
mps_res_t mps_definalize(mps_arena_t arena, mps_addr_t *refref)
|
|
{
|
|
Res res;
|
|
Addr object;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
object = (Addr)ArenaPeek(arena, (Ref *)refref);
|
|
res = ArenaDefinalize(arena, object);
|
|
|
|
ArenaLeave(arena);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* Messages */
|
|
|
|
|
|
void mps_message_type_enable(mps_arena_t arena,
|
|
mps_message_type_t mps_type)
|
|
{
|
|
MessageType type = (MessageType)mps_type;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
MessageTypeEnable(arena, type);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
void mps_message_type_disable(mps_arena_t arena,
|
|
mps_message_type_t mps_type)
|
|
{
|
|
MessageType type = (MessageType)mps_type;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
MessageTypeDisable(arena, type);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
mps_bool_t mps_message_poll(mps_arena_t arena)
|
|
{
|
|
Bool b;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
b = MessagePoll(arena);
|
|
|
|
ArenaLeave(arena);
|
|
return b;
|
|
}
|
|
|
|
mps_bool_t mps_message_queue_type(mps_message_type_t *mps_message_type_return,
|
|
mps_arena_t arena)
|
|
{
|
|
MessageType type;
|
|
Bool b;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
b = MessageQueueType(&type, arena);
|
|
|
|
ArenaLeave(arena);
|
|
if (b) {
|
|
*mps_message_type_return = (mps_message_type_t)type;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
mps_bool_t mps_message_get(mps_message_t *mps_message_return,
|
|
mps_arena_t arena,
|
|
mps_message_type_t mps_type)
|
|
{
|
|
Bool b;
|
|
MessageType type = (MessageType)mps_type;
|
|
Message message;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
b = MessageGet(&message, arena, type);
|
|
|
|
ArenaLeave(arena);
|
|
if (b) {
|
|
*mps_message_return = (mps_message_t)message;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
void mps_message_discard(mps_arena_t arena,
|
|
mps_message_t message)
|
|
{
|
|
ArenaEnter(arena);
|
|
|
|
MessageDiscard(arena, message);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* Message Methods */
|
|
|
|
/* -- All Message Types */
|
|
|
|
mps_message_type_t mps_message_type(mps_arena_t arena,
|
|
mps_message_t message)
|
|
{
|
|
MessageType type;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
type = MessageGetType(message);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
return (mps_message_type_t)type;
|
|
}
|
|
|
|
mps_clock_t mps_message_clock(mps_arena_t arena,
|
|
mps_message_t message)
|
|
{
|
|
Clock postedClock;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
postedClock = MessageGetClock(message);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
return (mps_clock_t)postedClock;
|
|
}
|
|
|
|
|
|
/* -- mps_message_type_finalization */
|
|
|
|
void mps_message_finalization_ref(mps_addr_t *mps_addr_return,
|
|
mps_arena_t arena,
|
|
mps_message_t message)
|
|
{
|
|
Ref ref;
|
|
|
|
AVER(mps_addr_return != NULL);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVERT(Arena, arena);
|
|
MessageFinalizationRef(&ref, arena, message);
|
|
ArenaPoke(arena, (Ref *)mps_addr_return, ref);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
/* -- mps_message_type_gc */
|
|
|
|
size_t mps_message_gc_live_size(mps_arena_t arena,
|
|
mps_message_t message)
|
|
{
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVERT(Arena, arena);
|
|
size = MessageGCLiveSize(message);
|
|
|
|
ArenaLeave(arena);
|
|
return (size_t)size;
|
|
}
|
|
|
|
size_t mps_message_gc_condemned_size(mps_arena_t arena,
|
|
mps_message_t message)
|
|
{
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVERT(Arena, arena);
|
|
size = MessageGCCondemnedSize(message);
|
|
|
|
ArenaLeave(arena);
|
|
return (size_t)size;
|
|
}
|
|
|
|
size_t mps_message_gc_not_condemned_size(mps_arena_t arena,
|
|
mps_message_t message)
|
|
{
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVERT(Arena, arena);
|
|
size = MessageGCNotCondemnedSize(message);
|
|
|
|
ArenaLeave(arena);
|
|
return (size_t)size;
|
|
}
|
|
|
|
/* -- mps_message_type_gc_start */
|
|
|
|
const char *mps_message_gc_start_why(mps_arena_t arena,
|
|
mps_message_t message)
|
|
{
|
|
const char *s;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVERT(Arena, arena);
|
|
|
|
s = MessageGCStartWhy(message);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/* Telemetry */
|
|
|
|
/* TODO: need to consider locking. See job003387, job003388. */
|
|
|
|
mps_word_t mps_telemetry_control(mps_word_t resetMask, mps_word_t flipMask)
|
|
{
|
|
return EventControl((Word)resetMask, (Word)flipMask);
|
|
}
|
|
|
|
void mps_telemetry_set(mps_word_t setMask)
|
|
{
|
|
(void)EventControl((Word)setMask, (Word)setMask);
|
|
}
|
|
|
|
void mps_telemetry_reset(mps_word_t resetMask)
|
|
{
|
|
(void)EventControl((Word)resetMask, 0);
|
|
}
|
|
|
|
mps_word_t mps_telemetry_get(void)
|
|
{
|
|
return EventControl(0, 0);
|
|
}
|
|
|
|
mps_label_t mps_telemetry_intern(const char *label)
|
|
{
|
|
AVER(label != NULL);
|
|
return (mps_label_t)EventInternString(label);
|
|
}
|
|
|
|
void mps_telemetry_label(mps_addr_t addr, mps_label_t intern_id)
|
|
{
|
|
EventLabelAddr((Addr)addr, (Word)intern_id);
|
|
}
|
|
|
|
void mps_telemetry_flush(void)
|
|
{
|
|
/* Telemetry does its own concurrency control, so none here. */
|
|
EventSync();
|
|
}
|
|
|
|
|
|
/* Allocation Patterns */
|
|
|
|
|
|
mps_alloc_pattern_t mps_alloc_pattern_ramp(void)
|
|
{
|
|
return (mps_alloc_pattern_t)AllocPatternRamp();
|
|
}
|
|
|
|
mps_alloc_pattern_t mps_alloc_pattern_ramp_collect_all(void)
|
|
{
|
|
return (mps_alloc_pattern_t)AllocPatternRampCollectAll();
|
|
}
|
|
|
|
|
|
/* mps_ap_alloc_pattern_begin -- signal start of an allocation pattern
|
|
*
|
|
* .ramp.hack: There are only two allocation patterns, both ramps. So
|
|
* we assume it's a ramp, and call BufferRampBegin/End directly, without
|
|
* dispatching. No point in creating a mechanism for that. */
|
|
|
|
mps_res_t mps_ap_alloc_pattern_begin(mps_ap_t mps_ap,
|
|
mps_alloc_pattern_t alloc_pattern)
|
|
{
|
|
Buffer buf;
|
|
Arena arena;
|
|
|
|
AVER(mps_ap != NULL);
|
|
buf = BufferOfAP(mps_ap);
|
|
AVER(TESTT(Buffer, buf));
|
|
|
|
arena = BufferArena(buf);
|
|
ArenaEnter(arena);
|
|
|
|
BufferRampBegin(buf, (AllocPattern)alloc_pattern);
|
|
|
|
ArenaLeave(arena);
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
mps_res_t mps_ap_alloc_pattern_end(mps_ap_t mps_ap,
|
|
mps_alloc_pattern_t alloc_pattern)
|
|
{
|
|
Buffer buf;
|
|
Arena arena;
|
|
Res res;
|
|
|
|
AVER(mps_ap != NULL);
|
|
buf = BufferOfAP(mps_ap);
|
|
AVER(TESTT(Buffer, buf));
|
|
UNUSED(alloc_pattern); /* .ramp.hack */
|
|
|
|
arena = BufferArena(buf);
|
|
ArenaEnter(arena);
|
|
|
|
res = BufferRampEnd(buf);
|
|
ArenaPoll(ArenaGlobals(arena)); /* .poll */
|
|
|
|
ArenaLeave(arena);
|
|
return res;
|
|
}
|
|
|
|
|
|
mps_res_t mps_ap_alloc_pattern_reset(mps_ap_t mps_ap)
|
|
{
|
|
Buffer buf;
|
|
Arena arena;
|
|
|
|
AVER(mps_ap != NULL);
|
|
buf = BufferOfAP(mps_ap);
|
|
AVER(TESTT(Buffer, buf));
|
|
|
|
arena = BufferArena(buf);
|
|
ArenaEnter(arena);
|
|
|
|
BufferRampReset(buf);
|
|
ArenaPoll(ArenaGlobals(arena)); /* .poll */
|
|
|
|
ArenaLeave(arena);
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
/* Low memory reservoir */
|
|
|
|
|
|
/* mps_reservoir_limit_set -- set the reservoir size */
|
|
|
|
void mps_reservoir_limit_set(mps_arena_t arena, size_t size)
|
|
{
|
|
ArenaEnter(arena);
|
|
ReservoirSetLimit(ArenaReservoir(arena), size);
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* mps_reservoir_limit -- return the reservoir size */
|
|
|
|
size_t mps_reservoir_limit(mps_arena_t arena)
|
|
{
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
size = ReservoirLimit(ArenaReservoir(arena));
|
|
|
|
ArenaLeave(arena);
|
|
return size;
|
|
}
|
|
|
|
|
|
/* mps_reservoir_available -- return memory available in the reservoir */
|
|
|
|
size_t mps_reservoir_available(mps_arena_t arena)
|
|
{
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
size = ReservoirAvailable(ArenaReservoir(arena));
|
|
|
|
ArenaLeave(arena);
|
|
return size;
|
|
}
|
|
|
|
|
|
/* Chains */
|
|
|
|
|
|
/* mps_chain_create -- create a chain */
|
|
|
|
mps_res_t mps_chain_create(mps_chain_t *chain_o, mps_arena_t arena,
|
|
size_t gen_count, mps_gen_param_s *params)
|
|
{
|
|
Chain chain;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(gen_count > 0);
|
|
res = ChainCreate(&chain, arena, gen_count, (GenParamStruct *)params);
|
|
|
|
ArenaLeave(arena);
|
|
if (res != ResOK)
|
|
return res;
|
|
*chain_o = (mps_chain_t)chain;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
|
|
/* mps_chain_destroy -- destroy a chain */
|
|
|
|
void mps_chain_destroy(mps_chain_t chain)
|
|
{
|
|
Arena arena;
|
|
|
|
AVER(TESTT(Chain, chain));
|
|
arena = chain->arena;
|
|
|
|
ArenaEnter(arena);
|
|
ChainDestroy(chain);
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* C. COPYRIGHT AND LICENSE
|
|
*
|
|
* Copyright (C) 2001-2014 Ravenbrook Limited <http://www.ravenbrook.com/>.
|
|
* All rights reserved. This is an open source license. Contact
|
|
* Ravenbrook for commercial licensing options.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Redistributions in any form must be accompanied by information on how
|
|
* to obtain complete source code for this software and any accompanying
|
|
* software that uses this software. The source code must either be
|
|
* included in the distribution or be available for no more than the cost
|
|
* of distribution plus a nominal fee, and must be freely redistributable
|
|
* under reasonable conditions. For an executable file, complete source
|
|
* code means the source code for all modules it contains. It does not
|
|
* include source code for modules or files that typically accompany the
|
|
* major components of the operating system on which the executable file
|
|
* runs.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|