/* mpsi.c: MEMORY POOL SYSTEM C INTERFACE LAYER
 *
 * $Id: //info.ravenbrook.com/project/mps/version/1.111/code/mpsi.c#1 $
 * Copyright (c) 2001-2003, 2006 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.
 *
 * .varargs: (rule.universal.complete) The varargs passed to
 * mps_alloc(_v) are ignored at the moment.  None of the pool
 * implementations use them.
 *
 * .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 "chain.h"

SRCID(mpsi, "$Id: //info.ravenbrook.com/project/mps/version/1.111/code/mpsi.c#1 $");


/* 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)
{
  /* .check.rc: Check that external and internal result codes match. */
  /* See <code/mps.h#result-codes> and <code/mpmtypes.h#result-codes>. */
  /* Also see .check.enum.cast. */
  CHECKL(COMPATTYPE(mps_res_t, Res));
  CHECKL((int)MPS_RES_OK == (int)ResOK);
  CHECKL((int)MPS_RES_FAIL == (int)ResFAIL);
  CHECKL((int)MPS_RES_RESOURCE == (int)ResRESOURCE);
  CHECKL((int)MPS_RES_MEMORY == (int)ResMEMORY);
  CHECKL((int)MPS_RES_LIMIT == (int)ResLIMIT);
  CHECKL((int)MPS_RES_UNIMPL == (int)ResUNIMPL);
  CHECKL((int)MPS_RES_IO == (int)ResIO);
  CHECKL((int)MPS_RES_COMMIT_LIMIT == (int)ResCOMMIT_LIMIT);

  /* 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 args;

  va_start(args, mps_arena_class);
  res = mps_arena_create_v(mps_arena_o, mps_arena_class, args);
  va_end(args);
  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 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 = ArenaCreateV(&arena, arena_class, 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_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_A != NULL);

  res = FormatCreate(&format,
                     arena,
                     (Align)mps_fmt_A->align,
                     FormatVarietyA,
                     mps_fmt_A->scan,
                     mps_fmt_A->skip,
                     mps_fmt_A->fwd,
                     mps_fmt_A->isfwd,
                     mps_fmt_A->copy,
                     mps_fmt_A->pad,
                     NULL,
                     (Size)0);

  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_B != NULL);

  res = FormatCreate(&format,
                     arena,
                     (Align)mps_fmt_B->align,
                     FormatVarietyB,
                     mps_fmt_B->scan,
                     mps_fmt_B->skip,
                     mps_fmt_B->fwd,
                     mps_fmt_B->isfwd,
                     mps_fmt_B->copy,
                     mps_fmt_B->pad,
                     mps_fmt_B->mps_class,
                     (Size)0);

  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 != NULL);

  res = FormatCreate(&format,
                     arena,
                     (Align)mps_fmt->align,
                     FormatVarietyAutoHeader,
                     mps_fmt->scan,
                     mps_fmt->skip,
                     mps_fmt->fwd,
                     mps_fmt->isfwd,
                     NULL,
                     mps_fmt->pad,
                     NULL,
                     (Size)mps_fmt->mps_headerSize);

  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_fixed != NULL);

  res = FormatCreate(&format,
                     arena,
                     (Align)mps_fmt_fixed->align,
                     FormatVarietyFixed,
                     mps_fmt_fixed->scan,
                     NULL,
                     mps_fmt_fixed->fwd,
                     mps_fmt_fixed->isfwd,
                     NULL,
                     mps_fmt_fixed->pad,
                     NULL,
                     (Size)0);

  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 args;
  va_start(args, mps_class);
  res = mps_pool_create_v(mps_pool_o, arena, mps_class, args);
  va_end(args);
  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 args)
{
  Pool pool;
  Res res;

  ArenaEnter(arena);

  AVER(mps_pool_o != NULL);
  AVERT(Arena, arena);
  AVERT(PoolClass, class);

  res = PoolCreateV(&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_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(PoolHasAddr(pool, p));
  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, ...)
{
  Arena arena;
  Buffer buf;
  BufferClass bufclass;
  Res res;
  va_list args;

  AVER(mps_ap_o != NULL);
  AVER(TESTT(Pool, pool));
  arena = PoolArena(pool);

  ArenaEnter(arena);

  AVERT(Pool, pool);

  va_start(args, pool);
  bufclass = PoolDefaultBufferClass(pool);
  res = BufferCreateV(&buf, bufclass, pool, TRUE, args);
  va_end(args);

  ArenaLeave(arena);

  if (res != ResOK)
    return res;
  *mps_ap_o = BufferAP(buf);
  return MPS_RES_OK;
}


/* 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 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 = BufferCreateV(&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_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)
{
  EventControl((Word)setMask, (Word)setMask);
}

void mps_telemetry_reset(mps_word_t resetMask)
{
  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-2003, 2006, 2008 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.
 */
