1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-25 15:00:45 -08:00

Merge branch/2016-09-06/job004006.

Copied from Perforce
 Change: 192365
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Gareth Rees 2016-09-13 17:32:07 +01:00
commit 62f3be6400
28 changed files with 418 additions and 95 deletions

View file

@ -82,6 +82,14 @@ static Res ArenaNoPagesMarkAllocated(Arena arena, Chunk chunk,
return ResUNIMPL;
}
static Bool ArenaNoChunkPageMapped(Chunk chunk, Index index)
{
UNUSED(chunk);
UNUSED(index);
NOTREACHED;
return FALSE;
}
static Res ArenaNoCreate(Arena *arenaReturn, ArgList args)
{
UNUSED(arenaReturn);
@ -122,6 +130,7 @@ DEFINE_CLASS(Arena, AbstractArena, klass)
klass->chunkFinish = ArenaNoChunkFinish;
klass->compact = ArenaTrivCompact;
klass->pagesMarkAllocated = ArenaNoPagesMarkAllocated;
klass->chunkPageMapped = ArenaNoChunkPageMapped;
klass->sig = ArenaClassSig;
}
@ -144,6 +153,7 @@ Bool ArenaClassCheck(ArenaClass klass)
CHECKL(FUNCHECK(klass->chunkFinish));
CHECKL(FUNCHECK(klass->compact));
CHECKL(FUNCHECK(klass->pagesMarkAllocated));
CHECKL(FUNCHECK(klass->chunkPageMapped));
CHECKS(ArenaClass, klass);
return TRUE;
}

View file

@ -383,6 +383,20 @@ static Res ClientArenaPagesMarkAllocated(Arena arena, Chunk chunk,
}
/* ClientChunkPageMapped -- determine if a page is mapped */
static Bool ClientChunkPageMapped(Chunk chunk, Index index)
{
UNUSED(chunk);
UNUSED(index);
AVERT(Chunk, chunk);
AVER(index < chunk->pages);
return TRUE;
}
/* ClientArenaFree - free a region in the arena */
static void ClientArenaFree(Addr base, Size size, Pool pool)
@ -443,6 +457,7 @@ DEFINE_CLASS(Arena, ClientArena, klass)
klass->free = ClientArenaFree;
klass->chunkInit = ClientChunkInit;
klass->chunkFinish = ClientChunkFinish;
klass->chunkPageMapped = ClientChunkPageMapped;
}

View file

@ -932,6 +932,16 @@ static Res VMPagesMarkAllocated(Arena arena, Chunk chunk,
}
static Bool VMChunkPageMapped(Chunk chunk, Index index)
{
VMChunk vmChunk;
AVERT(Chunk, chunk);
AVER(index < chunk->pages);
vmChunk = Chunk2VMChunk(chunk);
return BTGet(vmChunk->pages.mapped, index);
}
/* chunkUnmapAroundPage -- unmap spare pages in a chunk including this one
*
* Unmap the spare page passed, and possibly other pages in the chunk,
@ -1208,6 +1218,7 @@ DEFINE_CLASS(Arena, VMArena, klass)
klass->chunkFinish = VMChunkFinish;
klass->compact = VMCompact;
klass->pagesMarkAllocated = VMPagesMarkAllocated;
klass->chunkPageMapped = VMChunkPageMapped;
}

View file

@ -167,8 +167,9 @@
/* CONFIG_THREAD_SINGLE -- support single-threaded execution only
*
* This symbol causes the MPS to be built for single-threaded
* execution only, where locks are not needed and so lock operations
* can be defined as no-ops by lock.h.
* execution only, where locks are not needed and so the generic
* ("ANSI") lock module lockan.c can be used instead of the
* platform-specific lock module.
*/
#if !defined(CONFIG_THREAD_SINGLE)

View file

@ -409,7 +409,7 @@ void GlobalsPrepareToDestroy(Globals arenaGlobals)
arenaGlobals->defaultChain = NULL;
ChainDestroy(defaultChain);
LockRelease(arenaGlobals->lock);
ArenaLeave(arena);
/* Theoretically, another thread could grab the lock here, but it's */
/* not worth worrying about, since an attempt after the lock has been */
/* destroyed would lead to a crash just the same. */
@ -493,10 +493,9 @@ Ring GlobalsRememberedSummaryRing(Globals global)
/* ArenaEnter -- enter the state where you can look at the arena */
void (ArenaEnter)(Arena arena)
void ArenaEnter(Arena arena)
{
AVERT(Arena, arena);
ArenaEnter(arena);
ArenaEnterLock(arena, FALSE);
}
/* The recursive argument specifies whether to claim the lock
@ -541,10 +540,10 @@ void ArenaEnterRecursive(Arena arena)
/* ArenaLeave -- leave the state where you can look at MPM data structures */
void (ArenaLeave)(Arena arena)
void ArenaLeave(Arena arena)
{
AVERT(Arena, arena);
ArenaLeave(arena);
ArenaLeaveLock(arena, FALSE);
}
void ArenaLeaveLock(Arena arena, Bool recursive)
@ -574,6 +573,12 @@ void ArenaLeaveRecursive(Arena arena)
ArenaLeaveLock(arena, TRUE);
}
Bool ArenaBusy(Arena arena)
{
return LockIsHeld(ArenaGlobals(arena)->lock);
}
/* mps_exception_info -- pointer to exception info
*
* This is a hack to make exception info easier to find in a release

View file

@ -78,6 +78,11 @@ extern void LockRelease(Lock lock);
extern Bool LockCheck(Lock lock);
/* LockIsHeld -- test whether lock is held by any thread */
extern Bool LockIsHeld(Lock lock);
/* == Global locks == */
@ -123,26 +128,6 @@ extern void LockClaimGlobal(void);
extern void LockReleaseGlobal(void);
#if defined(LOCK)
/* Nothing to do: functions declared in all lock configurations. */
#elif defined(LOCK_NONE)
#define LockSize() MPS_PF_ALIGN
#define LockInit(lock) UNUSED(lock)
#define LockFinish(lock) UNUSED(lock)
#define LockClaimRecursive(lock) UNUSED(lock)
#define LockReleaseRecursive(lock) UNUSED(lock)
#define LockClaim(lock) UNUSED(lock)
#define LockRelease(lock) UNUSED(lock)
#define LockCheck(lock) ((void)lock, TRUE)
#define LockClaimGlobalRecursive()
#define LockReleaseGlobalRecursive()
#define LockClaimGlobal()
#define LockReleaseGlobal()
#else
#error "No lock configuration."
#endif /* LOCK */
#endif /* lock_h */

View file

@ -79,6 +79,12 @@ void (LockReleaseRecursive)(Lock lock)
--lock->claims;
}
Bool (LockIsHeld)(Lock lock)
{
AVERT(Lock, lock);
return lock->claims > 0;
}
/* Global locking is performed by normal locks.
* A separate lock structure is used for recursive and

View file

@ -39,20 +39,28 @@ int main(int argc, char *argv[])
Insist(b != NULL);
LockInit(a);
Insist(!LockIsHeld(a));
LockInit(b);
Insist(!LockIsHeld(b));
LockClaimGlobal();
LockClaim(a);
Insist(LockIsHeld(a));
LockClaimRecursive(b);
Insist(LockIsHeld(b));
LockClaimGlobalRecursive();
LockReleaseGlobal();
LockClaimGlobal();
LockRelease(a);
Insist(!LockIsHeld(a));
LockClaimGlobalRecursive();
LockReleaseGlobal();
LockClaimRecursive(b);
Insist(LockIsHeld(b));
LockFinish(a);
LockReleaseRecursive(b);
Insist(LockIsHeld(b));
LockReleaseRecursive(b);
Insist(!LockIsHeld(b));
LockFinish(b);
LockInit(a);
LockClaim(a);

View file

@ -45,6 +45,7 @@
SRCID(lockix, "$Id$");
#if defined(LOCK)
/* LockStruct -- the MPS lock structure
*
@ -185,6 +186,21 @@ void (LockReleaseRecursive)(Lock lock)
}
/* LockIsHeld -- test whether lock is held */
Bool (LockIsHeld)(Lock lock)
{
AVERT(Lock, lock);
if (pthread_mutex_trylock(&lock->mut) == 0) {
Bool claimed = lock->claims > 0;
int res = pthread_mutex_unlock(&lock->mut);
AVER(res == 0);
return claimed;
}
return TRUE;
}
/* Global locks
*
* .global: The two "global" locks are statically allocated normal locks.
@ -245,6 +261,13 @@ void (LockReleaseGlobal)(void)
}
#elif defined(LOCK_NONE)
#include "lockan.c"
#else
#error "No lock configuration."
#endif
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2016 Ravenbrook Limited <http://www.ravenbrook.com/>.

View file

@ -31,6 +31,7 @@
SRCID(lockw3, "$Id$");
#if defined(LOCK)
/* .lock.win32: Win32 lock structure; uses CRITICAL_SECTION */
typedef struct LockStruct {
@ -103,6 +104,15 @@ void (LockReleaseRecursive)(Lock lock)
LeaveCriticalSection(&lock->cs);
}
Bool (LockIsHeld)(Lock lock)
{
if (TryEnterCriticalSection(&lock->cs)) {
Bool claimed = lock->claims > 0;
LeaveCriticalSection(&lock->cs);
return claimed;
}
return TRUE;
}
/* Global locking is performed by normal locks.
@ -156,6 +166,13 @@ void (LockReleaseGlobal)(void)
}
#elif defined(LOCK_NONE)
#include "lockan.c"
#else
#error "No lock configuration."
#endif
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2016 Ravenbrook Limited <http://www.ravenbrook.com/>.

View file

@ -488,7 +488,6 @@ extern Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context);
extern Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit);
extern void ArenaFreeLandDelete(Arena arena, Addr base, Addr limit);
extern Bool GlobalsCheck(Globals arena);
extern Res GlobalsInit(Globals arena);
extern void GlobalsFinish(Globals arena);
@ -524,16 +523,12 @@ extern Bool ArenaGrainSizeCheck(Size size);
extern void ArenaEnterLock(Arena arena, Bool recursive);
extern void ArenaLeaveLock(Arena arena, Bool recursive);
extern void (ArenaEnter)(Arena arena);
extern void (ArenaLeave)(Arena arena);
extern void ArenaEnter(Arena arena);
extern void ArenaLeave(Arena arena);
extern void (ArenaPoll)(Globals globals);
#if defined(SHIELD)
#define ArenaEnter(arena) ArenaEnterLock(arena, FALSE)
#define ArenaLeave(arena) ArenaLeaveLock(arena, FALSE)
#elif defined(SHIELD_NONE)
#define ArenaEnter(arena) UNUSED(arena)
#define ArenaLeave(arena) AVER(arena->busyTraces == TraceSetEMPTY)
#define ArenaPoll(globals) UNUSED(globals)
#else
#error "No shield configuration."
@ -546,10 +541,12 @@ extern Bool (ArenaStep)(Globals globals, double interval, double multiplier);
extern void ArenaClamp(Globals globals);
extern void ArenaRelease(Globals globals);
extern void ArenaPark(Globals globals);
extern void ArenaPostmortem(Globals globals);
extern void ArenaExposeRemember(Globals globals, Bool remember);
extern void ArenaRestoreProtection(Globals globals);
extern Res ArenaStartCollect(Globals globals, int why);
extern Res ArenaCollect(Globals globals, int why);
extern Bool ArenaBusy(Arena arena);
extern Bool ArenaHasAddr(Arena arena, Addr addr);
extern Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr);
extern void ArenaChunkInsert(Arena arena, Chunk chunk);
@ -895,7 +892,7 @@ extern void (ShieldFlush)(Arena arena);
#define ShieldLower(arena, seg, mode) \
BEGIN UNUSED(arena); UNUSED(seg); UNUSED(mode); END
#define ShieldEnter(arena) BEGIN UNUSED(arena); END
#define ShieldLeave(arena) BEGIN UNUSED(arena); END
#define ShieldLeave(arena) AVER(arena->busyTraces == TraceSetEMPTY)
#define ShieldExpose(arena, seg) \
BEGIN UNUSED(arena); UNUSED(seg); END
#define ShieldCover(arena, seg) \

View file

@ -504,6 +504,7 @@ typedef struct mps_arena_class_s {
ArenaChunkFinishMethod chunkFinish;
ArenaCompactMethod compact;
ArenaPagesMarkAllocatedMethod pagesMarkAllocated;
ArenaChunkPageMappedMethod chunkPageMapped;
Sig sig;
} ArenaClassStruct;

View file

@ -124,6 +124,7 @@ typedef void (*ArenaCompactMethod)(Arena arena, Trace trace);
typedef Res (*ArenaPagesMarkAllocatedMethod)(Arena arena, Chunk chunk,
Index baseIndex, Count pages,
Pool pool);
typedef Bool (*ArenaChunkPageMappedMethod)(Chunk chunk, Index index);
/* These are not generally exposed and public, but are part of a commercial

View file

@ -435,6 +435,7 @@ typedef struct mps_fmt_fixed_s {
extern void mps_arena_clamp(mps_arena_t);
extern void mps_arena_release(mps_arena_t);
extern void mps_arena_park(mps_arena_t);
extern void mps_arena_postmortem(mps_arena_t);
extern void mps_arena_expose(mps_arena_t);
extern void mps_arena_unsafe_expose_remember_protection(mps_arena_t);
extern void mps_arena_unsafe_restore_protection(mps_arena_t);
@ -460,6 +461,7 @@ extern size_t mps_arena_spare_commit_limit(mps_arena_t);
extern double mps_arena_pause_time(mps_arena_t);
extern void mps_arena_pause_time_set(mps_arena_t, double);
extern mps_bool_t mps_arena_busy(mps_arena_t);
extern mps_bool_t mps_arena_has_addr(mps_arena_t, mps_addr_t);
extern mps_bool_t mps_addr_pool(mps_pool_t *, mps_arena_t, mps_addr_t);
extern mps_bool_t mps_addr_fmt(mps_fmt_t *, mps_arena_t, mps_addr_t);

View file

@ -260,6 +260,15 @@ void mps_arena_park(mps_arena_t arena)
}
void mps_arena_postmortem(mps_arena_t arena)
{
/* Don't call ArenaEnter -- one of the purposes of this function is
* to release the arena lock if it's held */
AVER(TESTT(Arena, arena));
ArenaPostmortem(ArenaGlobals(arena));
}
void mps_arena_expose(mps_arena_t arena)
{
ArenaEnter(arena);
@ -374,6 +383,17 @@ void mps_arena_destroy(mps_arena_t arena)
}
/* mps_arena_busy -- is the arena part way through an operation? */
mps_bool_t mps_arena_busy(mps_arena_t arena)
{
/* Don't call ArenaEnter -- the purpose of this function is to
* determine if the arena lock is held */
AVER(TESTT(Arena, arena));
return ArenaBusy(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)

View file

@ -450,11 +450,13 @@ static void *test(void *arg, size_t s)
mps_word_t c;
size_t r;
Insist(!mps_arena_busy(arena));
c = mps_collections(arena);
if(collections != c) {
collections = c;
printf("\nCollection %"PRIuLONGEST", %lu objects.\n", (ulongest_t)c, i);
printf("Collection %"PRIuLONGEST", %lu objects.\n", (ulongest_t)c, i);
for(r = 0; r < exactRootsCOUNT; ++r) {
cdie(exactRoots[r] == objNULL || dylan_check(exactRoots[r]),
"all roots check");
@ -565,7 +567,7 @@ int main(int argc, char *argv[])
MPS_ARGS_ADD(args, MPS_KEY_PAUSE_TIME, rnd_pause_time());
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, TEST_ARENA_SIZE);
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args),
"arena_create\n");
"arena_create");
} MPS_ARGS_END(args);
die(mps_thread_reg(&thread, arena), "thread_reg");
@ -591,9 +593,17 @@ int main(int argc, char *argv[])
}
mps_tramp(&r, test, arena, 0);
mps_root_destroy(reg_root);
mps_thread_dereg(thread);
mps_arena_destroy(arena);
switch (rnd() % 2) {
default:
case 0:
mps_root_destroy(reg_root);
mps_thread_dereg(thread);
mps_arena_destroy(arena);
break;
case 1:
mps_arena_postmortem(arena);
break;
}
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
return 0;

View file

@ -457,6 +457,7 @@ static mps_res_t scan1(mps_ss_t ss, mps_addr_t *objectIO)
static mps_res_t scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
{
Insist(mps_arena_busy(arena));
while(base < limit) {
mps_res_t res;

View file

@ -77,9 +77,12 @@ static void pad(mps_addr_t addr, size_t size)
}
}
static mps_arena_t arena;
static mps_res_t scan(mps_ss_t ss, mps_addr_t base,
mps_addr_t limit)
{
Insist(mps_arena_busy(arena));
MPS_SCAN_BEGIN(ss) {
mps_word_t *p = base;
while (p < (mps_word_t *)limit) {
@ -106,7 +109,7 @@ static mps_addr_t skip(mps_addr_t addr)
}
static void collect(mps_arena_t arena, size_t expected)
static void collect(size_t expected)
{
size_t finalized = 0;
mps_arena_collect(arena);
@ -148,7 +151,6 @@ static const char *mode_name[] = {
static void test(int mode)
{
mps_arena_t arena;
mps_thr_t thread;
mps_root_t root;
mps_fmt_t fmt;
@ -214,7 +216,7 @@ static void test(int mode)
die(mps_finalize(arena, &addr), "finalize");
}
collect(arena, expected);
collect(expected);
mps_arena_park(arena);
mps_ap_destroy(ap);

View file

@ -530,12 +530,11 @@ void TraceIdMessagesDestroy(Arena arena, TraceId ti)
/* -------- ArenaRelease, ArenaClamp, ArenaPark -------- */
/* ----- ArenaRelease, ArenaClamp, ArenaPark, ArenaPostmortem ----- */
/* ArenaRelease, ArenaClamp, ArenaPark -- allow/prevent collection work.
*
* These functions allow or prevent collection work.
/* ArenaRelease, ArenaClamp, ArenaPark, ArenaPostmortem --
* allow/prevent collection work.
*/
@ -596,6 +595,62 @@ void ArenaPark(Globals globals)
AVER(!ArenaEmergency(arena));
}
/* arenaExpose -- discard all protection from MPS-managed memory
*
* This is called by ArenaPostmortem, which we expect only to be used
* after a fatal error. So we use the lowest-level description of the
* MPS-managed memory (the chunk ring page tables) to avoid the risk
* of higher-level structures (like the segments) having been
* corrupted.
*
* After calling this function memory may not be in a consistent
* state, so it is not safe to continue running the MPS. If you need
* to expose memory but continue running the MPS, use
* ArenaExposeRemember instead.
*/
static void arenaExpose(Arena arena)
{
Ring node, next;
RING_FOR(node, &arena->chunkRing, next) {
Chunk chunk = RING_ELT(Chunk, arenaRing, node);
Index i;
for (i = 0; i < chunk->pages; ++i) {
if (Method(Arena, arena, chunkPageMapped)(chunk, i)) {
ProtSet(PageIndexBase(chunk, i), PageIndexBase(chunk, i + 1),
AccessSetEMPTY);
}
}
}
}
/* ArenaPostmortem -- enter the postmortem state */
void ArenaPostmortem(Globals globals)
{
Arena arena = GlobalsArena(globals);
/* Ensure lock is released. */
while (LockIsHeld(globals->lock)) {
LockReleaseRecursive(globals->lock);
}
/* Remove the arena from the global arena ring so that it no longer
* handles protection faults. (Don't call arenaDenounce because that
* needs to claim the global ring lock, but that might already be
* held, for example if we are inside ArenaAccess.) */
RingRemove(&globals->globalRing);
/* Clamp the arena so that ArenaPoll does nothing. */
ArenaClamp(globals);
/* Remove all protection from mapped pages. */
arenaExpose(arena);
}
/* ArenaStartCollect -- start a collection of everything in the
* arena; leave unclamped. */

View file

@ -542,7 +542,8 @@ platform instead.
_`.opt.thread`: ``CONFIG_THREAD_SINGLE`` causes the MPS to be built
for single-threaded execution only, where locks are not needed and so
lock operations can be defined as no-ops by ``lock.h``.
the generic ("ANSI") lock module ``lockan.c`` can be used instead of
the platform-specific lock module.
_`.opt.poll`: ``CONFIG_POLL_NONE`` causes the MPS to be built without
support for polling. This means that garbage collections will only

View file

@ -59,6 +59,13 @@ be claimed again by the thread currently holding them, without
blocking or deadlocking. (This is needed to implement the global
recursive lock.)
_`.req.held`: Provide a means to test if a lock is held. (This is
needed for debugging a dynamic function table callback on Windows on
x86-64. See ``mps_arena_busy()`` for a detailed description of this
use case. Note that in this use case the program is running
single-threaded and so there is no need for this feature to be
thread-safe.)
_`.req.global`: Provide *global* locks: that is locks that need not be
allocated or initialized by the user.
@ -124,6 +131,11 @@ thread and claims the lock (if not already held).
Restores the previous state of the lock remembered by the
corresponding ``LockClaimRecursive()`` call.
``Bool LockIsHeld(Lock lock)``
Return true if the lock is held by any thread, false otherwise. Note
that this function need not be thread-safe (see `.req.held`_).
``void LockClaimGlobal(void)``
Claims ownership of the binary global lock which was previously not

View file

@ -193,14 +193,14 @@ Memory Management Glossary: C
.. mps:specific::
One of the three states an :term:`arena` can be in (the
others being the :term:`unclamped state` and the
:term:`parked state`). In the clamped state, no object
motion occurs and the staleness of :term:`location
dependencies` does not change. However, a :term:`garbage
collection` may be in progress. Call
:c:func:`mps_arena_clamp` to put an arena into the clamped
state.
One of the four states an :term:`arena` can be in (the
others being the :term:`unclamped state`, the
:term:`parked state`, and the :term:`postmortem state`).
In the clamped state, no object motion occurs and the
staleness of :term:`location dependencies` does not
change. However, a :term:`garbage collection` may be in
progress. Call :c:func:`mps_arena_clamp` to put an arena
into the clamped state.
client arena

View file

@ -410,6 +410,7 @@ All
:term:`pointer`
:term:`pool`
:term:`pool class`
:term:`postmortem state`
:term:`precise garbage collection <exact garbage collection>`
:term:`precise reference <exact reference>`
:term:`precise root <exact root>`

View file

@ -179,14 +179,15 @@ Memory Management Glossary: P
.. mps:specific::
One of the three states an :term:`arena` can be in (the
others being the :term:`clamped state` and the
:term:`unclamped state`). In the parked state, no
:term:`garbage collection` is in progress, no object
motion occurs and the staleness of :term:`location
dependencies` does not change. Call
:c:func:`mps_arena_park` or :c:func:`mps_arena_collect` to
put an arena into the parked state.
One of the four states an :term:`arena` can be in (the
others being the :term:`clamped state`, the
:term:`postmortem state`, and the :term:`unclamped
state`). In the parked state, no :term:`garbage
collection` is in progress, no object motion occurs and
the staleness of :term:`location dependencies` does not
change. Call :c:func:`mps_arena_park` or
:c:func:`mps_arena_collect` to put an arena into the
parked state.
perfect fit
@ -402,6 +403,21 @@ Memory Management Glossary: P
class of :term:`pools` that manage memory according to
particular policy. See :ref:`pool`.
postmortem state
.. mps:specific::
One of the four states an :term:`arena` can be in (the
others being the :term:`unclamped state`, the
:term:`clamped state`, and the :term:`parked state`). In
the postmortem state, objects do not move in memory, the
staleness of :term:`location dependencies` does not
change, memory occupied by :term:`unreachable` objects is
not recycled, all memory protection is removed, and memory
may be in an inconsistent state. Call
:c:func:`mps_arena_postmortem` to put an arena into the
postmortem state.
precise garbage collection
.. see:: :term:`exact garbage collection`.

View file

@ -49,12 +49,12 @@ Memory Management Glossary: U
.. mps:specific::
One of the three states an :term:`arena` can be in (the
others being the :term:`clamped state` and the
:term:`parked state`). In the unclamped state, object
motion and other background activity may occur. Call
:c:func:`mps_arena_release` to put an arena into the
unclamped state.
One of the four states an :term:`arena` can be in (the
others being the :term:`clamped state`, the :term:`parked
state` and the :term:`postmortem state`). In the unclamped
state, object motion and other background activity may
occur. Call :c:func:`mps_arena_release` to put an arena
into the unclamped state.
undead

View file

@ -94,6 +94,31 @@ General debugging advice
On OS X, barrier hits do not use signals and so do not enter the
debugger.
#. .. index::
single: postmortem debugging
single: postmortem state
If the :term:`client program` is stopped in the debugger with the
MPS part of the way through execution of an operation in an
:term:`arena` (for example, a crash inside a :term:`scan method`),
it will not be possible to call introspection functions, such as
:c:func:`mps_arena_has_addr` or :c:func:`mps_addr_pool` (because
the MPS is not re-entrant), and it may not be possible to examine
some regions of memory (because they are :term:`protected` by the
MPS).
If you are in this situation and would like to be able to call MPS
functions or examine regions of memory from the debugger, then you
can put the arena into the :term:`postmortem state` by calling
:c:func:`mps_arena_postmortem` from the debugger. This unlocks the
arena and turns off protection.
.. warning::
After calling :c:func:`mps_arena_postmortem`, MPS-managed
memory is not in a consistent state, and so it is not safe to
continue running the client program.
.. index::
single: ASLR

View file

@ -25,6 +25,12 @@ New features
.. _LinuxThreads: http://pauillac.inria.fr/~xleroy/linuxthreads/
#. New function :c:func:`mps_arena_postmortem` assists with postmortem
debugging.
#. New function :c:func:`mps_arena_busy` assists debugging of re-entry
errors in dynamic function table callbacks on Windows on x86-64.
Interface changes
.................

View file

@ -430,12 +430,10 @@ Arena properties
operating system.
The function :c:func:`mps_arena_committed` may be called whatever
state the the arena is in (:term:`unclamped <unclamped state>`,
:term:`clamped <clamped state>`, or :term:`parked <parked
state>`). If it is called when the arena is in the unclamped state
then the value may change after this function returns. A possible
use might be to call it just after :c:func:`mps_arena_collect` to
estimate the size of the heap.
state the the arena is in. If it is called when the arena is in
the :term:`unclamped state` then the value may change after this
function returns. A possible use might be to call it just after
:c:func:`mps_arena_collect` to estimate the size of the heap.
If you want to know how much memory the MPS is using then you're
probably interested in the value :c:func:`mps_arena_committed`
@ -649,21 +647,41 @@ An arena is always in one of three states.
The *parked state* is the same as the clamped state, with the
additional constraint that no garbage collections are in progress.
#. .. index::
single: arena; postmortem state
single: postmortem state
In the *postmortem state*, incremental collection does not take
place, objects do not move in memory, references do not change, the
staleness of :term:`location dependencies` does not change, and
memory occupied by :term:`unreachable` objects is not recycled.
Additionally, all memory protection is removed, and memory may be
in an inconsistent state.
.. warning::
In this state, memory managed by the arena is not in a
consistent state, and so it is not safe to continue running the
client program. This state is intended for postmortem debugging
only.
Here's a summary:
============================================ ================================== ============================= ===========================
State unclamped clamped parked
============================================ ================================== ============================= ===========================
Collections may be running? yes yes no
New collections may start? yes no no
Objects may move? yes no no
Location dependencies may become stale? yes no no
Memory may be returned to the OS? yes no no
Functions that leave the arena in this state :c:func:`mps_arena_create_k`, :c:func:`mps_arena_clamp`, :c:func:`mps_arena_park`,
============================================ ================================== ============================= =========================== ==============================
State unclamped clamped parked postmortem
============================================ ================================== ============================= =========================== ==============================
Collections may be running? yes yes no yes
New collections may start? yes no no no
Objects may move? yes no no no
Location dependencies may become stale? yes no no no
Memory may be returned to the OS? yes no no no
Safe to continue running? yes yes yes no
Functions that leave the arena in this state :c:func:`mps_arena_create_k`, :c:func:`mps_arena_clamp`, :c:func:`mps_arena_park`, :c:func:`mps_arena_postmortem`
:c:func:`mps_arena_release`, :c:func:`mps_arena_step` :c:func:`mps_arena_collect`
:c:func:`mps_arena_start_collect`,
:c:func:`mps_arena_step`
============================================ ================================== ============================= ===========================
:c:func:`mps_arena_start_collect`,
:c:func:`mps_arena_step`
============================================ ================================== ============================= =========================== ==============================
The clamped and parked states are important when introspecting and
debugging. If you are examining the contents of the heap, you don't
@ -688,7 +706,7 @@ can only be called in this state.
Put an :term:`arena` into the :term:`clamped state`.
``arena`` is the arena to clamp.
``arena`` is the arena.
In the clamped state, no object motion will occur and the
staleness of :term:`location dependencies` will not change. All
@ -706,7 +724,7 @@ can only be called in this state.
Put an :term:`arena` into the :term:`parked state`.
``arena`` is the arena to park.
``arena`` is the arena.
While an arena is parked, no object motion will occur and the
staleness of :term:`location dependencies` will not change. All
@ -720,14 +738,42 @@ can only be called in this state.
.. c:function:: void mps_arena_release(mps_arena_t arena)
Puts an arena into the :term:`unclamped state`.
Put an arena into the :term:`unclamped state`.
``arena`` is the arena to unclamp.
``arena`` is the arena.
While an arena is unclamped, :term:`garbage collection`, object
motion, and other background activity can take place.
.. c:function:: void mps_arena_postmortem(mps_arena_t arena)
Put an arena into the :term:`postmortem state`.
``arena`` is the arena.
In the postmortem state, incremental collection does not take
place, objects do not move in memory, references do not change,
the staleness of :term:`location dependencies` does not change,
and memory occupied by :term:`unreachable` objects is not
recycled. Additionally, all memory protection is removed, and
memory may be in an inconsistent state.
.. warning::
1. After calling this function, memory managed by the arena is
not in a consistent state, and so it is no longer safe to
continue running the client program. This functions is
intended for postmortem debugging only.
2. This function must be called from the thread that holds the
arena lock (if any thread holds it). This is the case if the
program is single-threaded, or if it is called from an MPS
assertion handler. When calling this function from the
debugger, check the stack to see which thread has the MPS
arena lock.
.. index::
single: garbage collection; running
single: collection; running
@ -885,9 +931,10 @@ application.
.. index::
pair: arena; introspection
pair: arena; debugging
Arena introspection
-------------------
Arena introspection and debugging
---------------------------------
.. note::
@ -903,6 +950,51 @@ Arena introspection
address belongs.
.. c:function:: mps_bool_t mps_arena_busy(mps_arena_t arena)
Return true if an :term:`arena` is part of the way through
execution of an operation, false otherwise.
``arena`` is the arena.
.. note::
This function is intended to assist with debugging fatal
errors in the :term:`client program`. It is not expected to be
needed in normal use. If you find yourself wanting to use this
function other than in the use case described below, there may
be a better way to meet your requirements: please
:ref:`contact us <contact>`.
A debugger running on Windows on x86-64 needs to decode the
call stack, which it does by calling a callback that was
previously installed in the dynamic function table using
|RtlInstallFunctionTableCallback|_. If the debugger is entered
while the arena is busy, and if the callback needs to read
from MPS-managed memory, then it may attempt to re-enter the
MPS, which will fail as the MPS is not re-entrant.
.. |RtlInstallFunctionTableCallback| replace:: ``RtlInstallFunctionTableCallback()``
.. _RtlInstallFunctionTableCallback: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680595(v=vs.85).aspx
If this happens, in order to allow the debugger to finish
decoding the call stack, the only remedy is to put the arena
into the :term:`postmortem state`, so that memory is
:term:`unprotected` and objects do not move. So in your
dynamic function table callback, you might write::
if (mps_arena_busy(arena)) {
mps_arena_postmortem(arena);
}
.. warning::
This function only gives a reliable result in single-threaded
programs, and in multi-threaded programs where all threads but
one are known to be stopped (as they are when the debugger is
decoding the call stack in the use case described above).
.. c:function:: mps_bool_t mps_arena_has_addr(mps_arena_t arena, mps_addr_t addr)
Test whether an :term:`address` is managed by an :term:`arena`.