mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-24 06:20:43 -08:00
- new mps_message_type_gc_start() explains what triggered a collection; - design/message: add guide. Copied from Perforce Change: 161204 ServerID: perforce.ravenbrook.com
2003 lines
47 KiB
C
2003 lines
47 KiB
C
/* mpsi.c: MEMORY POOL SYSTEM C INTERFACE LAYER
|
|
*
|
|
* $Id$
|
|
* 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 "mpsavm.h" /* only for mps_space_create */
|
|
#include "sac.h"
|
|
#include "chain.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)
|
|
{
|
|
/* .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(CHECKTYPE(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 rank numbers match. */
|
|
/* See <code/mps.h#ranks> and <code/mpmtypes.h#ranks>. */
|
|
/* Also see .check.enum.cast. */
|
|
CHECKL(CHECKTYPE(mps_rank_t, Rank));
|
|
CHECKL((int)MPS_RANK_AMBIG == (int)RankAMBIG);
|
|
CHECKL((int)MPS_RANK_EXACT == (int)RankEXACT);
|
|
CHECKL((int)MPS_RANK_WEAK == (int)RankWEAK);
|
|
|
|
/* 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(CHECKTYPE(mps_word_t, Word));
|
|
|
|
/* The external idea of an address and the internal one */
|
|
/* had better match. */
|
|
CHECKL(CHECKTYPE(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(CHECKTYPE(size_t, Size));
|
|
|
|
/* Check ap_s/APStruct compatibility by hand */
|
|
/* .check.ap: See <code/mps.h#ap> and <code/buffer.h#ap>. */
|
|
CHECKL(sizeof(mps_ap_s) == sizeof(APStruct));
|
|
CHECKL(CHECKFIELD(mps_ap_s, init, APStruct, init));
|
|
CHECKL(CHECKFIELD(mps_ap_s, alloc, APStruct, alloc));
|
|
CHECKL(CHECKFIELD(mps_ap_s, limit, APStruct, limit));
|
|
|
|
/* Check sac_s/ExternalSACStruct compatibility by hand */
|
|
/* See <code/mps.h#sac> and <code/sac.h#sac>. */
|
|
CHECKL(sizeof(mps_sac_s) == sizeof(ExternalSACStruct));
|
|
CHECKL(CHECKFIELD(mps_sac_s, mps_middle, ExternalSACStruct, middle));
|
|
CHECKL(CHECKFIELD(mps_sac_s, mps_trapped,
|
|
ExternalSACStruct, trapped));
|
|
CHECKL(CHECKFIELDAPPROX(mps_sac_s, mps_freelists,
|
|
ExternalSACStruct, freelists));
|
|
CHECKL(sizeof(mps_sac_freelist_block_s)
|
|
== sizeof(SACFreeListBlockStruct));
|
|
CHECKL(CHECKFIELD(mps_sac_freelist_block_s, mps_size,
|
|
SACFreeListBlockStruct, size));
|
|
CHECKL(CHECKFIELD(mps_sac_freelist_block_s, mps_count,
|
|
SACFreeListBlockStruct, count));
|
|
CHECKL(CHECKFIELD(mps_sac_freelist_block_s, mps_count_max,
|
|
SACFreeListBlockStruct, countMax));
|
|
CHECKL(CHECKFIELD(mps_sac_freelist_block_s, mps_blocks,
|
|
SACFreeListBlockStruct, blocks));
|
|
|
|
/* Check sac_classes_s/SACClassesStruct compatibility by hand */
|
|
/* See <code/mps.h#sacc> and <code/sac.h#sacc>. */
|
|
CHECKL(sizeof(mps_sac_classes_s) == sizeof(SACClassesStruct));
|
|
CHECKL(CHECKFIELD(mps_sac_classes_s, mps_block_size,
|
|
SACClassesStruct, blockSize));
|
|
CHECKL(CHECKFIELD(mps_sac_classes_s, mps_cached_count,
|
|
SACClassesStruct, cachedCount));
|
|
CHECKL(CHECKFIELD(mps_sac_classes_s, mps_frequency,
|
|
SACClassesStruct, frequency));
|
|
|
|
/* Check ss_s/ScanStateStruct compatibility by hand */
|
|
/* .check.ss: See <code/mps.h#ss> and <code/mpmst.h#ss>. */
|
|
/* Note that the size of the mps_ss_s and ScanStateStruct */
|
|
/* are not equal. See <code/mpmst.h#ss>. CHECKFIELDAPPROX */
|
|
/* is used on the fix field because its type is punned and */
|
|
/* therefore isn't exactly checkable. See */
|
|
/* <design/interface-c/#pun.addr>. */
|
|
CHECKL(CHECKFIELDAPPROX(mps_ss_s, fix, ScanStateStruct, fix));
|
|
CHECKL(CHECKFIELD(mps_ss_s, w0, ScanStateStruct, zoneShift));
|
|
CHECKL(CHECKFIELD(mps_ss_s, w1, ScanStateStruct, white));
|
|
CHECKL(CHECKFIELD(mps_ss_s, w2, ScanStateStruct, unfixedSummary));
|
|
|
|
/* Check ld_s/LDStruct compatibility by hand */
|
|
/* .check.ld: See also <code/mpmst.h#ld.struct> and <code/mps.h#ld> */
|
|
CHECKL(sizeof(mps_ld_s) == sizeof(LDStruct));
|
|
CHECKL(CHECKFIELD(mps_ld_s, w0, LDStruct, epoch));
|
|
CHECKL(CHECKFIELD(mps_ld_s, w1, LDStruct, rs));
|
|
|
|
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 mps_arena,
|
|
mps_addr_t base, size_t size)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
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 mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
size = ArenaReserved(arena);
|
|
ArenaLeave(arena);
|
|
|
|
return (size_t)size;
|
|
}
|
|
|
|
/* for backward compatibility */
|
|
size_t mps_space_reserved(mps_space_t mps_space)
|
|
{
|
|
return mps_arena_reserved(mps_space);
|
|
}
|
|
|
|
size_t mps_arena_committed(mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
size = ArenaCommitted(arena);
|
|
ArenaLeave(arena);
|
|
|
|
return (size_t)size;
|
|
}
|
|
|
|
/* for backward compatibility */
|
|
size_t mps_space_committed(mps_space_t mps_space)
|
|
{
|
|
return mps_arena_committed(mps_space);
|
|
}
|
|
|
|
size_t mps_arena_spare_committed(mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
size = ArenaSpareCommitted(arena);
|
|
ArenaLeave(arena);
|
|
|
|
return (size_t)size;
|
|
}
|
|
|
|
size_t mps_arena_commit_limit(mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Size size;
|
|
|
|
ArenaEnter(arena);
|
|
size = ArenaCommitLimit(arena);
|
|
ArenaLeave(arena);
|
|
|
|
return size;
|
|
}
|
|
|
|
mps_res_t mps_arena_commit_limit_set(mps_arena_t mps_arena, size_t limit)
|
|
{
|
|
Res res;
|
|
Arena arena = (Arena)mps_arena;
|
|
|
|
ArenaEnter(arena);
|
|
res = ArenaSetCommitLimit(arena, limit);
|
|
ArenaLeave(arena);
|
|
|
|
return res;
|
|
}
|
|
|
|
void mps_arena_spare_commit_limit_set(mps_arena_t mps_arena, size_t limit)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
|
|
ArenaEnter(arena);
|
|
ArenaSetSpareCommitLimit(arena, limit);
|
|
ArenaLeave(arena);
|
|
|
|
return;
|
|
}
|
|
|
|
size_t mps_arena_spare_commit_limit(mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
size_t limit;
|
|
|
|
ArenaEnter(arena);
|
|
limit = ArenaSpareCommitLimit(arena);
|
|
ArenaLeave(arena);
|
|
|
|
return limit;
|
|
}
|
|
|
|
void mps_arena_clamp(mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
ArenaEnter(arena);
|
|
ArenaClamp(ArenaGlobals(arena));
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
/* for backward compatibility */
|
|
void mps_space_clamp(mps_space_t mps_space)
|
|
{
|
|
mps_arena_clamp(mps_space);
|
|
}
|
|
|
|
|
|
void mps_arena_release(mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
ArenaEnter(arena);
|
|
ArenaRelease(ArenaGlobals(arena));
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
/* for backward compatibility */
|
|
void mps_space_release(mps_space_t mps_space)
|
|
{
|
|
mps_arena_release(mps_space);
|
|
}
|
|
|
|
|
|
void mps_arena_park(mps_space_t mps_space)
|
|
{
|
|
Arena arena = (Arena)mps_space;
|
|
ArenaEnter(arena);
|
|
ArenaPark(ArenaGlobals(arena));
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
/* for backward compatibility */
|
|
void mps_space_park(mps_space_t mps_space)
|
|
{
|
|
mps_arena_park(mps_space);
|
|
}
|
|
|
|
void mps_arena_expose(mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_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 mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
ArenaEnter(arena);
|
|
ArenaExposeRemember(ArenaGlobals(arena), 1);
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
void mps_arena_unsafe_restore_protection(mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
ArenaEnter(arena);
|
|
ArenaRestoreProtection(ArenaGlobals(arena));
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
mps_res_t mps_arena_start_collect(mps_space_t mps_space)
|
|
{
|
|
Res res;
|
|
Arena arena = (Arena)mps_space;
|
|
ArenaEnter(arena);
|
|
res = ArenaStartCollect(ArenaGlobals(arena), TraceStartWhyCLIENTFULL_INCREMENTAL);
|
|
ArenaLeave(arena);
|
|
return res;
|
|
}
|
|
|
|
mps_res_t mps_arena_collect(mps_space_t mps_space)
|
|
{
|
|
Res res;
|
|
Arena arena = (Arena)mps_space;
|
|
ArenaEnter(arena);
|
|
res = ArenaCollect(ArenaGlobals(arena), TraceStartWhyCLIENTFULL_BLOCK);
|
|
ArenaLeave(arena);
|
|
return res;
|
|
}
|
|
|
|
mps_bool_t mps_arena_step(mps_arena_t mps_arena,
|
|
double interval,
|
|
double multiplier)
|
|
{
|
|
Bool b;
|
|
Arena arena = (Arena)mps_arena;
|
|
ArenaEnter(arena);
|
|
b = ArenaStep(ArenaGlobals(arena), interval, multiplier);
|
|
ArenaLeave(arena);
|
|
return b;
|
|
}
|
|
|
|
/* for backward compatibility */
|
|
mps_res_t mps_space_collect(mps_space_t mps_space)
|
|
{
|
|
return mps_arena_collect(mps_space);
|
|
}
|
|
|
|
|
|
/* 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 mps_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, (ArenaClass)mps_arena_class, args);
|
|
if (res != ResOK)
|
|
return res;
|
|
|
|
ArenaLeave(arena);
|
|
*mps_arena_o = (mps_arena_t)arena;
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
/* DEPRECATED */
|
|
mps_res_t mps_space_create(mps_space_t *mps_space_o)
|
|
{
|
|
return mps_arena_create(mps_space_o, mps_arena_class_vm(), ARENA_SIZE);
|
|
}
|
|
|
|
|
|
/* mps_arena_destroy -- destroy an arena object */
|
|
|
|
void mps_arena_destroy(mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
|
|
ArenaEnter(arena);
|
|
ArenaDestroy(arena);
|
|
}
|
|
|
|
/* DEPRECATED */
|
|
void mps_space_destroy(mps_space_t mps_space)
|
|
{
|
|
mps_arena_destroy(mps_space);
|
|
}
|
|
|
|
|
|
/* mps_arena_has_addr -- is this address managed by this arena? */
|
|
|
|
mps_bool_t mps_arena_has_addr(mps_arena_t mps_arena, mps_addr_t p)
|
|
{
|
|
Bool b;
|
|
Arena arena = (Arena)mps_arena;
|
|
|
|
/* 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_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 mps_arena,
|
|
mps_fmt_A_s *mps_fmt_A)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Format format;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_fmt_A != NULL);
|
|
|
|
res = FormatCreate(&format,
|
|
arena,
|
|
(Align)mps_fmt_A->align,
|
|
FormatVarietyA,
|
|
(FormatScanMethod)mps_fmt_A->scan,
|
|
(FormatSkipMethod)mps_fmt_A->skip,
|
|
(FormatMoveMethod)mps_fmt_A->fwd,
|
|
(FormatIsMovedMethod)mps_fmt_A->isfwd,
|
|
(FormatCopyMethod)mps_fmt_A->copy,
|
|
(FormatPadMethod)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 mps_arena,
|
|
mps_fmt_B_s *mps_fmt_B)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Format format;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_fmt_B != NULL);
|
|
|
|
res = FormatCreate(&format,
|
|
arena,
|
|
(Align)mps_fmt_B->align,
|
|
FormatVarietyB,
|
|
(FormatScanMethod)mps_fmt_B->scan,
|
|
(FormatSkipMethod)mps_fmt_B->skip,
|
|
(FormatMoveMethod)mps_fmt_B->fwd,
|
|
(FormatIsMovedMethod)mps_fmt_B->isfwd,
|
|
(FormatCopyMethod)mps_fmt_B->copy,
|
|
(FormatPadMethod)mps_fmt_B->pad,
|
|
(FormatClassMethod)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 mps_arena,
|
|
mps_fmt_auto_header_s *mps_fmt)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Format format;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_fmt != NULL);
|
|
|
|
res = FormatCreate(&format,
|
|
arena,
|
|
(Align)mps_fmt->align,
|
|
FormatVarietyAutoHeader,
|
|
(FormatScanMethod)mps_fmt->scan,
|
|
(FormatSkipMethod)mps_fmt->skip,
|
|
(FormatMoveMethod)mps_fmt->fwd,
|
|
(FormatIsMovedMethod)mps_fmt->isfwd,
|
|
NULL,
|
|
(FormatPadMethod)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 mps_arena,
|
|
mps_fmt_fixed_s *mps_fmt_fixed)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Format format;
|
|
Res res;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVER(mps_fmt_fixed != NULL);
|
|
|
|
res = FormatCreate(&format,
|
|
arena,
|
|
(Align)mps_fmt_fixed->align,
|
|
FormatVarietyFixed,
|
|
(FormatScanMethod)mps_fmt_fixed->scan,
|
|
NULL,
|
|
(FormatMoveMethod)mps_fmt_fixed->fwd,
|
|
(FormatIsMovedMethod)mps_fmt_fixed->isfwd,
|
|
NULL,
|
|
(FormatPadMethod)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 mps_fmt)
|
|
{
|
|
Format format = (Format)mps_fmt;
|
|
Arena arena;
|
|
|
|
AVER(CHECKT(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 mps_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, mps_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 mps_arena,
|
|
mps_class_t mps_class, va_list args)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Pool pool;
|
|
PoolClass class = (PoolClass)mps_class;
|
|
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 mps_pool)
|
|
{
|
|
Pool pool = (Pool)mps_pool;
|
|
Arena arena;
|
|
|
|
AVER(CHECKT(Pool, pool));
|
|
arena = PoolArena(pool);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
PoolDestroy(pool);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
mps_res_t mps_alloc(mps_addr_t *p_o, mps_pool_t mps_pool, size_t size, ...)
|
|
{
|
|
Pool pool = (Pool)mps_pool;
|
|
Arena arena;
|
|
Addr p;
|
|
Res res;
|
|
|
|
AVER(CHECKT(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 mps_pool, mps_addr_t p, size_t size)
|
|
{
|
|
Pool pool = (Pool)mps_pool;
|
|
Arena arena;
|
|
|
|
AVER(CHECKT(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 mps_pool, ...)
|
|
{
|
|
Pool pool = (Pool)mps_pool;
|
|
Arena arena;
|
|
Buffer buf;
|
|
BufferClass bufclass;
|
|
Res res;
|
|
va_list args;
|
|
|
|
AVER(mps_ap_o != NULL);
|
|
AVER(CHECKT(Pool, pool));
|
|
arena = PoolArena(pool);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVERT(Pool, pool);
|
|
|
|
va_start(args, mps_pool);
|
|
bufclass = PoolDefaultBufferClass(pool);
|
|
res = BufferCreateV(&buf, bufclass, pool, TRUE, args);
|
|
va_end(args);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK)
|
|
return res;
|
|
*mps_ap_o = (mps_ap_t)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 mps_pool,
|
|
va_list args)
|
|
{
|
|
Pool pool = (Pool)mps_pool;
|
|
Arena arena;
|
|
Buffer buf;
|
|
BufferClass bufclass;
|
|
Res res;
|
|
|
|
AVER(mps_ap_o != NULL);
|
|
AVER(CHECKT(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 = (mps_ap_t)BufferAP(buf);
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
void mps_ap_destroy(mps_ap_t mps_ap)
|
|
{
|
|
Buffer buf = BufferOfAP((AP)mps_ap);
|
|
Arena arena;
|
|
|
|
AVER(mps_ap != NULL);
|
|
AVER(CHECKT(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(CHECKT(Buffer, BufferOfAP((AP)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(CHECKT(Buffer, BufferOfAP((AP)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(CHECKT(Buffer, BufferOfAP((AP)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((AP)mps_ap);
|
|
Arena arena;
|
|
AllocFrame frame;
|
|
Res res;
|
|
|
|
AVER(CHECKT(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((AP)mps_ap);
|
|
Arena arena;
|
|
Res res;
|
|
|
|
AVER(CHECKT(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: Note that mps_ap_fill should never be "called"
|
|
* directly by the client code. It is invoked by the mps_reserve macro. */
|
|
|
|
mps_res_t mps_ap_fill(mps_addr_t *p_o, mps_ap_t mps_ap, size_t size)
|
|
{
|
|
Buffer buf = BufferOfAP((AP)mps_ap);
|
|
Arena arena;
|
|
Addr p;
|
|
Res res;
|
|
|
|
AVER(mps_ap != NULL);
|
|
AVER(CHECKT(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((AP)mps_ap);
|
|
Arena arena;
|
|
Addr p;
|
|
Res res;
|
|
|
|
AVER(mps_ap != NULL);
|
|
AVER(CHECKT(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: Note that mps_ap_trip should never be "called"
|
|
* directly by the client code. It is invoked by the mps_commit macro. */
|
|
|
|
mps_bool_t mps_ap_trip(mps_ap_t mps_ap, mps_addr_t p, size_t size)
|
|
{
|
|
Buffer buf = BufferOfAP((AP)mps_ap);
|
|
Arena arena;
|
|
Bool b;
|
|
|
|
AVER(mps_ap != NULL);
|
|
AVER(CHECKT(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 mps_pool,
|
|
size_t classes_count, mps_sac_classes_s *mps_classes)
|
|
{
|
|
Pool pool = (Pool)mps_pool;
|
|
Arena arena;
|
|
SACClasses classes;
|
|
SAC sac;
|
|
Res res;
|
|
|
|
AVER(mps_sac_o != NULL);
|
|
AVER(CHECKT(Pool, pool));
|
|
arena = PoolArena(pool);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
classes = (SACClasses)mps_classes;
|
|
res = SACCreate(&sac, pool, (Count)classes_count, classes);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
if (res != ResOK) return (mps_res_t)res;
|
|
*mps_sac_o = (mps_sac_t)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((ExternalSAC)mps_sac);
|
|
Arena arena;
|
|
|
|
AVER(CHECKT(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((ExternalSAC)mps_sac);
|
|
Arena arena;
|
|
|
|
AVER(CHECKT(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((ExternalSAC)mps_sac);
|
|
Arena arena;
|
|
Addr p;
|
|
Res res;
|
|
|
|
AVER(p_o != NULL);
|
|
AVER(CHECKT(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((ExternalSAC)mps_sac);
|
|
Arena arena;
|
|
|
|
AVER(CHECKT(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(CHECKT(SAC, SACOfExternalSAC((ExternalSAC)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(CHECKT(SAC, SACOfExternalSAC((ExternalSAC)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 mps_arena,
|
|
mps_rank_t mps_rank, mps_rm_t mps_rm,
|
|
mps_root_scan_t mps_root_scan, void *p, size_t s)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
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,
|
|
(RootScanMethod)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 mps_arena,
|
|
mps_rank_t mps_rank, mps_rm_t mps_rm,
|
|
mps_addr_t *base, size_t size)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
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 mps_arena,
|
|
mps_rank_t mps_rank, mps_rm_t mps_rm,
|
|
mps_addr_t *base, size_t size,
|
|
mps_word_t mask)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
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 mps_arena,
|
|
mps_rank_t mps_rank, mps_rm_t mps_rm,
|
|
mps_fmt_scan_t mps_fmt_scan,
|
|
mps_addr_t base, mps_addr_t limit)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Rank rank = (Rank)mps_rank;
|
|
FormatScanMethod scan = (FormatScanMethod)mps_fmt_scan;
|
|
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 mps_arena,
|
|
mps_rank_t mps_rank, mps_rm_t mps_rm,
|
|
mps_thr_t mps_thr, mps_reg_scan_t mps_reg_scan,
|
|
void *reg_scan_p, size_t mps_size)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Rank rank = (Rank)mps_rank;
|
|
Thread thread = (Thread)mps_thr;
|
|
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,
|
|
(RootScanRegMethod)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 mps_thr, void *p, size_t s)
|
|
{
|
|
ScanState ss = (ScanState)mps_ss;
|
|
Thread thread = (Thread)mps_thr;
|
|
|
|
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 mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_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 mps_thr)
|
|
{
|
|
Thread thread = (Thread)mps_thr;
|
|
Arena arena;
|
|
|
|
AVER(ThreadCheckSimple(thread));
|
|
arena = ThreadArena(thread);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
ThreadDeregister(thread, arena);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
void mps_ld_reset(mps_ld_t mps_ld, mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
LD ld = (LD)mps_ld;
|
|
|
|
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 mps_ld, mps_arena_t mps_arena, mps_addr_t addr)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
LD ld = (LD)mps_ld;
|
|
|
|
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 mps_ld, mps_arena_t mps_arena,
|
|
mps_ld_t mps_from)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
LD ld = (LD)mps_ld;
|
|
LD from = (LD)mps_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 mps_ld, mps_arena_t mps_arena,
|
|
mps_addr_t addr)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
LD ld = (LD)mps_ld;
|
|
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 mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_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 mps_arena, mps_addr_t *refref)
|
|
{
|
|
Res res;
|
|
Addr object;
|
|
Arena arena = (Arena)mps_arena;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
object = (Addr)ArenaPeek(arena, (Addr)refref);
|
|
res = ArenaFinalize(arena, object);
|
|
|
|
ArenaLeave(arena);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* mps_definalize -- deregister for finalization */
|
|
|
|
mps_res_t mps_definalize(mps_arena_t mps_arena, mps_addr_t *refref)
|
|
{
|
|
Res res;
|
|
Addr object;
|
|
Arena arena = (Arena)mps_arena;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
object = (Addr)ArenaPeek(arena, (Addr)refref);
|
|
res = ArenaDefinalize(arena, object);
|
|
|
|
ArenaLeave(arena);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* Messages */
|
|
|
|
|
|
mps_bool_t mps_message_poll(mps_arena_t mps_arena)
|
|
{
|
|
Bool b;
|
|
Arena arena = (Arena)mps_arena;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
b = MessagePoll(arena);
|
|
|
|
ArenaLeave(arena);
|
|
return b;
|
|
}
|
|
|
|
|
|
mps_message_type_t mps_message_type(mps_arena_t mps_arena,
|
|
mps_message_t mps_message)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Message message = (Message)mps_message;
|
|
MessageType type;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
type = MessageGetType(message);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
return (mps_message_type_t)type;
|
|
}
|
|
|
|
void mps_message_discard(mps_arena_t mps_arena,
|
|
mps_message_t mps_message)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Message message = (Message)mps_message;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
MessageDiscard(arena, message);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
void mps_message_type_enable(mps_arena_t mps_arena,
|
|
mps_message_type_t mps_type)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
MessageType type = (MessageType)mps_type;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
MessageTypeEnable(arena, type);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
void mps_message_type_disable(mps_arena_t mps_arena,
|
|
mps_message_type_t mps_type)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
MessageType type = (MessageType)mps_type;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
MessageTypeDisable(arena, type);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
mps_bool_t mps_message_get(mps_message_t *mps_message_return,
|
|
mps_arena_t mps_arena,
|
|
mps_message_type_t mps_type)
|
|
{
|
|
Bool b;
|
|
Arena arena = (Arena)mps_arena;
|
|
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;
|
|
}
|
|
|
|
mps_bool_t mps_message_queue_type(mps_message_type_t *mps_message_type_return,
|
|
mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_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;
|
|
}
|
|
|
|
|
|
/* Message-Type-Specific Methods */
|
|
|
|
/* MPS_MESSAGE_TYPE_FINALIZATION */
|
|
|
|
void mps_message_finalization_ref(mps_addr_t *mps_addr_return,
|
|
mps_arena_t mps_arena,
|
|
mps_message_t mps_message)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Message message = (Message)mps_message;
|
|
Ref ref;
|
|
|
|
AVER(mps_addr_return != NULL);
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVERT(Arena, arena);
|
|
MessageFinalizationRef(&ref, arena, message);
|
|
ArenaPoke(arena, (Addr)mps_addr_return, ref);
|
|
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
/* MPS_MESSAGE_TYPE_GC */
|
|
|
|
size_t mps_message_gc_live_size(mps_arena_t mps_arena,
|
|
mps_message_t mps_message)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Message message = (Message)mps_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 mps_arena,
|
|
mps_message_t mps_message)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Message message = (Message)mps_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 mps_arena,
|
|
mps_message_t mps_message)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
Message message = (Message)mps_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 mps_arena,
|
|
mps_message_t mps_message)
|
|
{
|
|
const char *s;
|
|
Arena arena = (Arena)mps_arena;
|
|
Message message = (Message)mps_message;
|
|
|
|
ArenaEnter(arena);
|
|
|
|
AVERT(Arena, arena);
|
|
|
|
s = MessageGCStartWhy(message);
|
|
|
|
ArenaLeave(arena);
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Telemetry */
|
|
|
|
mps_word_t mps_telemetry_control(mps_word_t resetMask, mps_word_t flipMask)
|
|
{
|
|
/* Doesn't require locking and isn't arena-specific. */
|
|
return EventControl((Word)resetMask, (Word)flipMask);
|
|
}
|
|
|
|
mps_word_t mps_telemetry_intern(const char *label)
|
|
{
|
|
AVER(label != NULL);
|
|
return (mps_word_t)EventInternString(label);
|
|
}
|
|
|
|
void mps_telemetry_label(mps_addr_t addr, mps_word_t intern_id)
|
|
{
|
|
EventLabelAddr((Addr)addr, (Word)intern_id);
|
|
}
|
|
|
|
void mps_telemetry_flush(void)
|
|
{
|
|
/* Telemetry does its own concurrency control, so none here. */
|
|
(void)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((AP)mps_ap);
|
|
AVER(CHECKT(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((AP)mps_ap);
|
|
AVER(CHECKT(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((AP)mps_ap);
|
|
AVER(CHECKT(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 mps_arena, size_t size)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
|
|
ArenaEnter(arena);
|
|
ReservoirSetLimit(ArenaReservoir(arena), size);
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* mps_reservoir_limit -- return the reservoir size */
|
|
|
|
size_t mps_reservoir_limit(mps_arena_t mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_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 mps_arena)
|
|
{
|
|
Arena arena = (Arena)mps_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 mps_arena,
|
|
size_t gen_count, mps_gen_param_s *params)
|
|
{
|
|
Arena arena = (Arena)mps_arena;
|
|
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 mps_chain)
|
|
{
|
|
Arena arena;
|
|
Chain chain = (Chain)mps_chain;
|
|
|
|
AVER(CHECKT(Chain, chain));
|
|
arena = chain->arena;
|
|
|
|
ArenaEnter(arena);
|
|
ChainDestroy(chain);
|
|
ArenaLeave(arena);
|
|
}
|
|
|
|
|
|
/* C. COPYRIGHT AND LICENSE
|
|
*
|
|
* Copyright (C) 2001-2003, 2006 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.
|
|
*/
|