1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-30 12:21:25 -08:00

Because of the requirement to make popped objects dead in a timely fashion, we can only implement lightweight pops to an address in the buffer. in particular we can't support pending pops, which means that there's no need for the fields in the allocation point that support pending pops.

Copied from Perforce
 Change: 187232
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Gareth Rees 2014-10-13 12:46:28 +01:00
parent fce7e87366
commit 9dc8bd1e70
10 changed files with 58 additions and 361 deletions

View file

@ -30,10 +30,6 @@
SRCID(buffer, "$Id$");
/* forward declarations */
static void BufferFrameNotifyPopPending(Buffer buffer);
/* BufferCheck -- check consistency of a buffer
*
* See .ap.async. */
@ -52,15 +48,6 @@ Bool BufferCheck(Buffer buffer)
CHECKL(buffer->emptySize <= buffer->fillSize);
CHECKL(buffer->alignment == buffer->pool->alignment);
CHECKL(AlignCheck(buffer->alignment));
CHECKL(BoolCheck(buffer->ap_s._enabled));
if (buffer->ap_s._enabled) {
/* no useful check for frameptr - mutator may be updating it */
CHECKL(BoolCheck(buffer->ap_s._lwpoppending));
} else {
CHECKL(buffer->ap_s._lwpoppending == FALSE);
CHECKL(buffer->ap_s._frameptr == NULL);
}
/* If any of the buffer's fields indicate that it is reset, make */
/* sure it is really reset. Otherwise, check various properties */
@ -81,8 +68,6 @@ Bool BufferCheck(Buffer buffer)
/* Nothing reliable to check for lightweight frame state */
CHECKL(buffer->poolLimit == (Addr)0);
} else {
Addr aplimit;
/* The buffer is attached to a region of memory. */
/* Check consistency. */
CHECKL(buffer->mode & BufferModeATTACHED);
@ -101,14 +86,6 @@ Bool BufferCheck(Buffer buffer)
CHECKL(AddrIsAligned(buffer->ap_s.limit, buffer->alignment));
CHECKL(AddrIsAligned(buffer->poolLimit, buffer->alignment));
/* .lwcheck: If LW frames are enabled, the buffer may become */
/* trapped asynchronously. It can't become untrapped */
/* asynchronously, though. See <design/alloc-frame/#lw-frame.pop>. */
/* Read a snapshot value of the limit field. Use this to determine */
/* if we are trapped, and to permit more useful checking when not */
/* yet trapped. */
aplimit = buffer->ap_s.limit;
/* If the buffer isn't trapped then "limit" should be the limit */
/* set by the owning pool. Otherwise, "init" is either at the */
/* same place it was at flip (.commit.before) or has been set */
@ -119,12 +96,10 @@ Bool BufferCheck(Buffer buffer)
/* request.dylan.170429.sol.zero_). */
/* .. _request.dylan.170429.sol.zero: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/170429 */
if ((buffer->ap_s._enabled && aplimit == (Addr)0) /* see .lwcheck */
|| (!buffer->ap_s._enabled && BufferIsTrapped(buffer))) {
if (BufferIsTrapped(buffer)) {
/* .check.use-trapped: This checking function uses BufferIsTrapped, */
/* So BufferIsTrapped can't do checking as that would cause an */
/* infinite loop. */
CHECKL(aplimit == (Addr)0);
if (buffer->mode & BufferModeFLIPPED) {
CHECKL(buffer->ap_s.init == buffer->initAtFlip
|| buffer->ap_s.init == buffer->ap_s.alloc);
@ -133,7 +108,6 @@ Bool BufferCheck(Buffer buffer)
}
/* Nothing special to check in the logged mode. */
} else {
CHECKL(aplimit == buffer->poolLimit); /* see .lwcheck */
CHECKL(buffer->initAtFlip == (Addr)0);
}
}
@ -230,9 +204,6 @@ static Res BufferInit(Buffer buffer, BufferClass class,
buffer->ap_s.init = (mps_addr_t)0;
buffer->ap_s.alloc = (mps_addr_t)0;
buffer->ap_s.limit = (mps_addr_t)0;
buffer->ap_s._frameptr = NULL;
buffer->ap_s._enabled = FALSE;
buffer->ap_s._lwpoppending = FALSE;
buffer->poolLimit = (Addr)0;
buffer->rampCount = 0;
@ -319,8 +290,6 @@ void BufferDetach(Buffer buffer, Pool pool)
/* Ask the owning pool to do whatever it needs to before the */
/* buffer is detached (e.g. copy buffer state into pool state). */
(*pool->class->bufferEmpty)(pool, buffer, init, limit);
/* Use of lightweight frames must have been disabled by now */
AVER(BufferFrameState(buffer) == BufferFrameDISABLED);
/* run any class-specific detachment method */
buffer->class->detach(buffer);
@ -346,7 +315,6 @@ void BufferDetach(Buffer buffer, Pool pool)
buffer->poolLimit = (Addr)0;
buffer->mode &=
~(BufferModeATTACHED|BufferModeFLIPPED|BufferModeTRANSITION);
BufferFrameSetState(buffer, BufferFrameDISABLED);
EVENT2(BufferEmpty, buffer, spare);
}
@ -383,11 +351,6 @@ void BufferFinish(Buffer buffer)
AVER(BufferIsReady(buffer));
/* <design/alloc-frame/#lw-frame.sync.trip> */
if (BufferIsTrappedByMutator(buffer)) {
BufferFrameNotifyPopPending(buffer);
}
BufferDetach(buffer, pool);
/* Dispatch to the buffer class method to perform any */
@ -463,44 +426,6 @@ static void BufferSetUnflipped(Buffer buffer)
}
/* BufferFrameState
*
* Returns the frame state of a buffer. See
* <design/alloc-frame/#lw-frame.states>. */
FrameState BufferFrameState(Buffer buffer)
{
AVERT(Buffer, buffer);
if (buffer->ap_s._enabled) {
if (buffer->ap_s._lwpoppending) {
return BufferFramePOP_PENDING;
} else {
AVER(buffer->ap_s._frameptr == NULL);
return BufferFrameVALID;
}
} else {
AVER(buffer->ap_s._frameptr == NULL);
AVER(buffer->ap_s._lwpoppending == FALSE);
return BufferFrameDISABLED;
}
}
/* BufferFrameSetState
*
* Sets the frame state of a buffer. Only the mutator may set the
* PopPending state. See <design/alloc-frame/#lw-frame.states>. */
void BufferFrameSetState(Buffer buffer, FrameState state)
{
AVERT(Buffer, buffer);
AVER(state == BufferFrameVALID || state == BufferFrameDISABLED);
buffer->ap_s._frameptr = NULL;
buffer->ap_s._lwpoppending = FALSE;
buffer->ap_s._enabled = (state == BufferFrameVALID);
}
/* BufferSetAllocAddr
*
* Sets the init & alloc pointers of a buffer. */
@ -518,32 +443,6 @@ void BufferSetAllocAddr(Buffer buffer, Addr addr)
}
/* BufferFrameNotifyPopPending
*
* Notifies the pool when a lightweight frame pop operation has been
* deferred and needs to be processed. See
* <design/alloc-frame/#lw-frame.sync.trip>. */
static void BufferFrameNotifyPopPending(Buffer buffer)
{
AllocFrame frame;
Pool pool;
AVER(BufferIsTrappedByMutator(buffer));
AVER(BufferFrameState(buffer) == BufferFramePOP_PENDING);
frame = (AllocFrame)buffer->ap_s._frameptr;
/* Unset PopPending state & notify the pool */
BufferFrameSetState(buffer, BufferFrameVALID);
/* If the frame is no longer trapped, undo the trap by resetting */
/* the AP limit pointer */
if (!BufferIsTrapped(buffer)) {
buffer->ap_s.limit = buffer->poolLimit;
}
pool = BufferPool(buffer);
(*pool->class->framePopPending)(pool, buffer, frame);
}
/* BufferFramePush
*
* See <design/alloc-frame/>. */
@ -555,17 +454,12 @@ Res BufferFramePush(AllocFrame *frameReturn, Buffer buffer)
AVER(frameReturn != NULL);
/* Process any flip or PopPending */
/* Process any flip */
if (!BufferIsReset(buffer) && buffer->ap_s.limit == (Addr)0) {
/* .fill.unflip: If the buffer is flipped then we unflip the buffer. */
if (buffer->mode & BufferModeFLIPPED) {
BufferSetUnflipped(buffer);
}
/* check for PopPending */
if (BufferIsTrappedByMutator(buffer)) {
BufferFrameNotifyPopPending(buffer);
}
}
pool = BufferPool(buffer);
return (*pool->class->framePush)(frameReturn, pool, buffer);
@ -702,11 +596,6 @@ Res BufferFill(Addr *pReturn, Buffer buffer, Size size,
BufferSetUnflipped(buffer);
}
/* <design/alloc-frame/#lw-frame.sync.trip> */
if (BufferIsTrappedByMutator(buffer)) {
BufferFrameNotifyPopPending(buffer);
}
/* .fill.logged: If the buffer is logged then we leave it logged. */
next = AddrAdd(buffer->ap_s.alloc, size);
if (next > (Addr)buffer->ap_s.alloc &&
@ -807,8 +696,6 @@ Bool BufferTrip(Buffer buffer, Addr p, Size size)
AVER(buffer->ap_s.limit == 0);
/* Of course we should be trapped. */
AVER(BufferIsTrapped(buffer));
/* But the mutator shouldn't have caused the trap */
AVER(!BufferIsTrappedByMutator(buffer));
/* The init and alloc fields should be equal at this point, because */
/* the step .commit.update has happened. */
@ -955,21 +842,7 @@ void BufferReassignSeg(Buffer buffer, Seg seg)
Bool BufferIsTrapped(Buffer buffer)
{
/* Can't check buffer, see .check.use-trapped */
return BufferIsTrappedByMutator(buffer)
|| ((buffer->mode & (BufferModeFLIPPED|BufferModeLOGGED)) != 0);
}
/* BufferIsTrappedByMutator
*
* Indicates whether the mutator trapped the buffer. See
* <design/alloc-frame/#lw-frame.sync.trip> and .ap.async. */
Bool BufferIsTrappedByMutator(Buffer buffer)
{
AVER(!buffer->ap_s._lwpoppending || buffer->ap_s._enabled);
/* Can't check buffer, see .check.use-trapped */
return buffer->ap_s._lwpoppending;
return (buffer->mode & (BufferModeFLIPPED|BufferModeLOGGED)) != 0;
}

View file

@ -273,8 +273,6 @@ extern Res PoolNoFramePush(AllocFrame *frameReturn, Pool pool, Buffer buf);
extern Res PoolTrivFramePush(AllocFrame *frameReturn, Pool pool, Buffer buf);
extern Res PoolNoFramePop(Pool pool, Buffer buf, AllocFrame frame);
extern Res PoolTrivFramePop(Pool pool, Buffer buf, AllocFrame frame);
extern void PoolNoFramePopPending(Pool pool, Buffer buf, AllocFrame frame);
extern void PoolTrivFramePopPending(Pool pool, Buffer buf, AllocFrame frame);
extern Res PoolNoAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr);
extern void PoolNoWalk(Pool pool, Seg seg, FormattedObjectsVisitor f,
void *p, size_t s);
@ -800,7 +798,6 @@ extern Addr BufferScanLimit(Buffer buffer);
extern void BufferReassignSeg(Buffer buffer, Seg seg);
extern Bool BufferIsTrapped(Buffer buffer);
extern Bool BufferIsTrappedByMutator(Buffer buffer);
extern void BufferRampBegin(Buffer buffer, AllocPattern pattern);
extern Res BufferRampEnd(Buffer buffer);

View file

@ -74,7 +74,6 @@ typedef struct mps_pool_class_s {
PoolRampEndMethod rampEnd; /* end a ramp pattern */
PoolFramePushMethod framePush; /* push an allocation frame */
PoolFramePopMethod framePop; /* pop an allocation frame */
PoolFramePopPendingMethod framePopPending; /* notify pending pop */
PoolAddrObjectMethod addrObject; /* find client pointer to object */
PoolWalkMethod walk; /* walk over a segment */
PoolFreeWalkMethod freewalk; /* walk over free blocks */

View file

@ -226,8 +226,6 @@ typedef Res (*PoolFramePushMethod)(AllocFrame *frameReturn,
Pool pool, Buffer buf);
typedef Res (*PoolFramePopMethod)(Pool pool, Buffer buf,
AllocFrame frame);
typedef void (*PoolFramePopPendingMethod)(Pool pool, Buffer buf,
AllocFrame frame);
typedef Res (*PoolAddrObjectMethod)(Addr *pReturn,
Pool pool, Seg seg, Addr addr);
typedef void (*PoolWalkMethod)(Pool pool, Seg seg, FormattedObjectsVisitor f,
@ -321,14 +319,6 @@ enum {
#define BufferModeTRANSITION ((BufferMode)(1<<3))
/* Buffer frame states. See <design/alloc-frame/#lw-frame.states> */
enum {
BufferFrameVALID = 1,
BufferFramePOP_PENDING,
BufferFrameDISABLED
};
/* Rank constants -- see <design/type/#rank> */
/* These definitions must match <code/mps.h#rank>. */
/* This is checked by <code/mpsi.c#check>. */

View file

@ -305,9 +305,6 @@ typedef struct mps_ap_s { /* allocation point descriptor */
mps_addr_t init; /* limit of initialized memory */
mps_addr_t alloc; /* limit of allocated memory */
mps_addr_t limit; /* limit of available memory */
mps_addr_t _frameptr; /* lightweight frame pointer */
mps_bool_t _enabled; /* lightweight frame status */
mps_bool_t _lwpoppending; /* lightweight pop pending? */
} mps_ap_s;

View file

@ -950,7 +950,7 @@ mps_res_t (mps_ap_frame_push)(mps_frame_t *frame_o, mps_ap_t mps_ap)
return MPS_RES_FAIL;
}
if (!mps_ap->_lwpoppending && mps_ap->init < mps_ap->limit) {
if (mps_ap->init < mps_ap->limit) {
/* Valid state for a lightweight push */
*frame_o = (mps_frame_t)mps_ap->init;
return MPS_RES_OK;
@ -996,14 +996,12 @@ mps_res_t (mps_ap_frame_pop)(mps_ap_t mps_ap, mps_frame_t frame)
buf = BufferOfAP(mps_ap);
AVER(TESTT(Buffer, buf));
if (mps_ap->_enabled
&& BufferBase(buf) <= (Addr)frame
/* FIXME: is it thread-safe to read BufferBase here? */
if (BufferBase(buf) <= (Addr)frame
&& (mps_addr_t)frame < mps_ap->init)
{
/* Lightweight pop to address in same buffer */
mps_ap->_frameptr = (mps_addr_t)frame; /* record pending pop */
mps_ap->_lwpoppending = TRUE;
mps_ap->limit = (mps_addr_t)0; /* trap the buffer */
/* Lightweight pop to earlier address in same buffer */
mps_ap->init = mps_ap->alloc = (mps_addr_t)frame;
return MPS_RES_OK;
} else {

View file

@ -65,7 +65,6 @@ Bool PoolClassCheck(PoolClass class)
CHECKL(FUNCHECK(class->rampEnd));
CHECKL(FUNCHECK(class->framePush));
CHECKL(FUNCHECK(class->framePop));
CHECKL(FUNCHECK(class->framePopPending));
CHECKL(FUNCHECK(class->addrObject));
CHECKL(FUNCHECK(class->walk));
CHECKL(FUNCHECK(class->freewalk));

View file

@ -57,7 +57,6 @@ void PoolClassMixInBuffer(PoolClass class)
/* By default, buffered pools treat frame operations as NOOPs */
class->framePush = PoolTrivFramePush;
class->framePop = PoolTrivFramePop;
class->framePopPending = PoolTrivFramePopPending;
class->bufferClass = BufferClassGet;
}
@ -138,7 +137,6 @@ DEFINE_CLASS(AbstractPoolClass, class)
class->rampEnd = PoolNoRampEnd;
class->framePush = PoolNoFramePush;
class->framePop = PoolNoFramePop;
class->framePopPending = PoolNoFramePopPending;
class->addrObject = PoolNoAddrObject;
class->walk = PoolNoWalk;
class->freewalk = PoolTrivFreeWalk;
@ -598,16 +596,6 @@ Res PoolNoFramePop(Pool pool, Buffer buf, AllocFrame frame)
}
void PoolNoFramePopPending(Pool pool, Buffer buf, AllocFrame frame)
{
AVERT(Pool, pool);
AVERT(Buffer, buf);
/* frame is of an abstract type & can't be checked */
UNUSED(frame);
NOTREACHED;
}
Res PoolTrivFramePush(AllocFrame *frameReturn, Pool pool, Buffer buf)
{
AVER(frameReturn != NULL);
@ -627,16 +615,6 @@ Res PoolTrivFramePop(Pool pool, Buffer buf, AllocFrame frame)
}
void PoolTrivFramePopPending(Pool pool, Buffer buf, AllocFrame frame)
{
AVERT(Pool, pool);
AVERT(Buffer, buf);
/* frame is of an abstract type & can't be checked */
UNUSED(frame);
NOOP;
}
Res PoolNoAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr)
{
AVER(pReturn != NULL);

View file

@ -462,8 +462,6 @@ found:
AVERT(Seg, seg);
/* put the segment on the buffer chain */
sncRecordAllocatedSeg(buffer, seg);
/* Permit the use of lightweight frames - .lw-frame-state */
BufferFrameSetState(buffer, BufferFrameVALID);
*baseReturn = SegBase(seg);
*limitReturn = SegLimit(seg);
return ResOK;
@ -485,9 +483,6 @@ static void SNCBufferEmpty(Pool pool, Buffer buffer,
AVER(SegLimit(seg) == limit);
snc = PoolSNC(pool);
AVERT(SNC, snc);
AVER(BufferFrameState(buffer) == BufferFrameVALID);
/* .lw-frame-state */
BufferFrameSetState(buffer, BufferFrameDISABLED);
arena = BufferArena(buffer);
@ -503,7 +498,7 @@ static void SNCBufferEmpty(Pool pool, Buffer buffer,
/* SNCScanLimit -- limit of scannable objects in segment */
static Addr SNCScanLimit(Arena arena, Seg seg)
static Addr SNCScanLimit(Seg seg)
{
Addr limit;
Buffer buf;
@ -511,27 +506,9 @@ static Addr SNCScanLimit(Arena arena, Seg seg)
if (buf == NULL) {
/* Segment is unbuffered: entire segment scannable */
limit = SegLimit(seg);
} else if (BufferFrameState(buf) != BufferFramePOP_PENDING) {
/* No pop pending: scannable up to limit of initialized objects. */
limit = BufferScanLimit(buf);
} else {
Addr addr = (Addr)buf->ap_s._frameptr;
if (addr == NULL) {
/* Pop pending to bottom of stack */
limit = BufferBase(buf);
} else {
Seg popSeg;
Bool foundSeg = SegOfAddr(&popSeg, arena, addr);
AVER(foundSeg);
if (popSeg == seg) {
/* Pop pending to address in same segment */
AVER(addr <= BufferScanLimit(buf)); /* check direction of pop */
limit = addr;
} else {
/* Pop pending to address in different segment */
limit = BufferBase(buf);
}
}
/* Segment is buffered: scannable up to limit of initialized objects. */
limit = BufferScanLimit(buf);
}
return limit;
}
@ -553,7 +530,7 @@ static Res SNCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg)
format = pool->format;
base = SegBase(seg);
limit = SNCScanLimit(PoolArena(pool), seg);
limit = SNCScanLimit(seg);
if (base < limit) {
res = (*format->scan)(&ss->ss_s, base, limit);
@ -575,16 +552,11 @@ static Res SNCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg)
static Res SNCFramePush(AllocFrame *frameReturn, Pool pool, Buffer buf)
{
FrameState state;
AVER(frameReturn != NULL);
AVERT(Pool, pool);
AVERT(Buffer, buf);
state = BufferFrameState(buf);
/* Should have been notified of pending pops before this */
AVER(state == BufferFrameVALID || state == BufferFrameDISABLED);
if (state == BufferFrameDISABLED) {
AVER(BufferIsReset(buf)); /* The buffer must be reset */
if (BufferIsReset(buf)) {
AVER(sncBufferTopSeg(buf) == NULL); /* The stack must be empty */
/* Use NULL to indicate an empty stack. .lw-frame-null */
*frameReturn = NULL;
@ -609,7 +581,7 @@ static Res SNCFramePush(AllocFrame *frameReturn, Pool pool, Buffer buf)
}
static void SNCFramePopPending(Pool pool, Buffer buf, AllocFrame frame)
static Res SNCFramePop(Pool pool, Buffer buf, AllocFrame frame)
{
Addr addr;
SNC snc;
@ -618,8 +590,6 @@ static void SNCFramePopPending(Pool pool, Buffer buf, AllocFrame frame)
/* frame is an Addr and can't be directly checked */
snc = PoolSNC(pool);
AVERT(SNC, snc);
AVER(BufferFrameState(buf) == BufferFrameVALID);
if (frame == NULL) {
/* corresponds to a pop to bottom of stack. .lw-frame-null */
@ -646,19 +616,9 @@ static void SNCFramePopPending(Pool pool, Buffer buf, AllocFrame frame)
BufferDetach(buf, pool);
sncPopPartialSegChain(snc, buf, seg);
BufferAttach(buf, SegBase(seg), SegLimit(seg), addr, (Size)0);
/* Permit the use of lightweight frames - .lw-frame-state */
BufferFrameSetState(buf, BufferFrameVALID);
}
}
}
static Res SNCFramePop(Pool pool, Buffer buf, AllocFrame frame)
{
AVERT(Pool, pool);
AVERT(Buffer, buf);
BufferFrameSetState(buf, BufferFrameVALID);
SNCFramePopPending(pool, buf, frame);
return ResOK;
}
@ -683,7 +643,7 @@ static void SNCWalk(Pool pool, Seg seg, FormattedObjectsVisitor f,
snc = PoolSNC(pool);
AVERT(SNC, snc);
format = pool->format;
limit = SNCScanLimit(PoolArena(pool), seg);
limit = SNCScanLimit(seg);
while(object < limit) {
(*f)(object, format, pool, p, s);
@ -759,7 +719,6 @@ DEFINE_POOL_CLASS(SNCPoolClass, this)
this->scan = SNCScan;
this->framePush = SNCFramePush;
this->framePop = SNCFramePop;
this->framePopPending = SNCFramePopPending;
this->walk = SNCWalk;
this->bufferClass = SNCBufClassGet;
this->totalSize = SNCTotalSize;

View file

@ -170,11 +170,11 @@ as parameters.
_`.op.obligatory`: The following operations are supported on any
allocation point which supports allocation frames:-
_`.operation.push`: The ``PushFrame()`` operation creates a new
_`.operation.push`: The *FramePush* operation creates a new
allocation frame of the currently chosen frame class, makes this new
frame the current frame, and returns a handle for the frame.
_`.operation.pop`: The ``PopFrame()`` operation takes a frame handle
_`.operation.pop`: The *FramePop* operation takes a frame handle
as a parameter. Some pool classes might insist or assume that this is
the handle for the current frame. It finds the parent of that frame
and makes it the current frame. The operation indicates that all
@ -190,23 +190,23 @@ allocation frames, but not all. Pools may choose to support some or
all of these operations for certain frame classes. An unsupported
operation will return a failure value:-
_`.operation.select`: The ``SelectFrame()`` operation takes a frame
_`.operation.select`: The *FrameSelect* operation takes a frame
handle as a parameter and makes that frame the current frame. It does
not indicate that any children of the current frame contain objects
which are likely to be dead.
_`.operation.select-addr`: The ``SelectFrameOfAddr()`` operation takes
_`.operation.select-addr`: The *FrameSelectOfAddr* operation takes
an address as a parameter and makes the frame of that address the
current frame. It does not indicate that any children of the current
frame contain objects which are likely to be dead.
_`.operation.in-frame`: The ``AddrInFrame()`` operation determines
_`.operation.in-frame`: The *FrameHasAddr()* operation determines
whether the supplied address is the address of an object allocated in
the supplied frame, or any child of that frame.
_`.operation.set`: The ``SetFrameClass()`` operation takes a frame
_`.operation.set`: The *SetFrameClass* operation takes a frame
class and an allocation point as parameters, and makes that the
current frame class for the allocation point. The next ``PushFrame()``
current frame class for the allocation point. The next *FramePush*
operation will create a new frame of that class.
@ -231,32 +231,32 @@ External functions
..................
_`.fn.client.push`: ``mps_ap_frame_push()`` is used by clients to
invoke the ``PushFrame()`` operation. For lightweight frames, this
invoke the *FramePush* operation. For lightweight frames, this
might not invoke the corresponding internal function.
_`.fn.client.pop`: ``mps_ap_frame_pop()`` is used by clients to invoke
the ``PopFrame()`` operation. For lightweight frames, this might not
the *FramePop* operation. For lightweight frames, this might not
invoke the corresponding internal function.
``mps_res_t mps_ap_frame_select(mps_ap_t buf, mps_frame_t frame)``
_`.fn.client.select`: This following function is used by clients to
invoke the ``SelectFrame()`` operation.
invoke the *FrameSelect* operation.
``mps_res_t mps_ap_frame_select_from_addr(mps_ap_t buf, mps_addr_t addr)``
_`.fn.client.select-addr`: This function is used by clients to invoke
the ``SelectFrameOfAddr()`` operation.
the *FrameSelectOfAddr* operation.
``mps_res_t mps_ap_addr_in_frame(mps_bool_t *inframe_o, mps_ap_t buf, mps_addr_t *addrref, mps_frame_t frame)``
_`.fn.client.in-frame`: This function is used by clients to invoke the
``AddrInFrame()`` operation.
*FrameHasAddr()* operation.
``mps_res_t mps_ap_set_frame_class(mps_ap_t buf, mps_frame_class_t class)``
_`.fn.client.set`: This function is used by clients to invoke the
``SetFrameClass()`` operation.
*SetFrameClass* operation.
``mps_frame_class_t mps_alloc_frame_class_stack(void)``
@ -278,32 +278,32 @@ _`.type.frame-class`: Frame classes are defined as an abstract type.
``typedef Res (*PoolFramePushMethod)(AllocFrame *frameReturn, Pool pool, Buffer buf)``
_`.fn.push`: A pool method of this type is called (if needed) to
invoke the ``PushFrame()`` operation.
invoke the *FramePush* operation.
``typedef Res (*PoolFramePopMethod)(Pool pool, Buffer buf, AllocFrame frame)``
_`.fn.pop`: A pool method of this type is called (if needed)
to invoke the PopFrame operation:
to invoke the *FramePop* operation:
``typedef Res (*PoolFrameSelectMethod)(Pool pool, Buffer buf, AllocFrame frame)``
_`.fn.select`: A pool method of this type is called to invoke the
``SelectFrame()`` operation.
*FrameSelect* operation.
``typedef Res (*PoolFrameSelectFromAddrMethod)(Pool pool, Buffer buf, Addr addr)``
_`.fn.select-addr`: A pool method of this type is called to invoke the
``SelectFrameOfAddr()`` operation.
*FrameSelectOfAddr* operation.
``typedef Res (*PoolAddrInFrameMethod)(Bool *inframeReturn, Pool pool, Seg seg, Addr *addrref, AllocFrame frame)``
``typedef Res (*PoolFrameHasAddrMethod)(Bool *inframeReturn, Pool pool, Seg seg, Addr *addrref, AllocFrame frame)``
_`.fn.in-frame`: A pool method of this type is called to invoke the
``AddrInFrame()`` operation.
*FrameHasAddr()* operation.
``typedef Res (*PoolSetFrameClassMethod)(Pool pool, Buffer buf, AllocFrameClass class)``
_`.fn.set`: A pool method of this type is called to invoke the
``SetFrameClass()`` operation.
*SetFrameClass* operation.
Lightweight frames
@ -313,96 +313,23 @@ Overview
........
_`.lw-frame.overview`: Allocation points provide direct support for
lightweight frames, and are designed to permit PushFrame and PopFrame
operations without the need for locking and delegation to the pool
method. Pools can disable this mechanism for any allocation point, so
that the pool method is always called. The pool method will be called
whenever synchronization is required for other reasons (e.g. the
buffer is tripped).
lightweight frames, and are designed to permit *FramePush* and
*FramePop* operations without the need for locking and delegation to
the pool method. The pool method will be called whenever
synchronization is required for other reasons (e.g. the buffer is
tripped).
_`.lw-frame.model`: Lightweight frames offer direct support for a
particular model of allocation frame use, whereby the PushFrame
particular model of allocation frame use, whereby the *FramePush*
operation returns the current allocation pointer as a frame handle,
and the PopFrame operation causes the allocation pointer to be reset
and the *FramePop* operation causes the allocation pointer to be reset
to the address of the frame handle. This model should be suitable for
simple stack frames, where more advanced operations like SelectFrame
simple stack frames, where more advanced operations like *FrameSelect*
are not supported. It may also be suitable for more advanced
allocation frame models when they are being used simply. The use of a
complex operation always involves synchronization via locking, and the
pool may disable lightweight synchronization temporarily at this time.
State
.....
_`.lw-frame.states`: Allocation points supporting lightweight frames
will be in one of the following states:
============ ================================================================
Valid Indicates that ``PushFrame()`` can be a lightweight
operation and need not be synchronized.
PopPending Indicates that there has been a ``PopFrame()`` operation
that the pool must respond to.
Disabled Indicates that the pool has disabled support for lightweight
operations for this AP.
============ ================================================================
These states are in addition to the state normally held by an AP for
allocation purposes. An AP will be in the Disabled state at creation.
_`.lw-frame.transitions`: State transitions happen under the following
circumstances:
======================= ====================================================
Valid → PopPending As a result of a client ``PopFrame()``
operation.
Valid → Disabled At the choice of the pool (for example, when
responding to a ``SelectFrame()`` operation).
PopPending → Valid At the choice of the pool, when processing a
``PopFrame()``.
PopPending → Disabled At the choice of the pool, when processing a
``PopFrame()``.
Disabled → Valid At the choice of the pool.
Disabled → Popframe Illegal.
======================= ====================================================
_`.lw-frame.state-impl`: Each AP contains 3 additional fields to hold this state::
mps_addr_t frameptr;
mps_bool_t enabled;
mps_bool_t lwPopPending;
_`.lw-frame.enabled`: The ``enabled`` slot holds the following values for
each state:
========== ==========
Valid ``TRUE``
PopPending ``TRUE``
Disabled ``FALSE``
========== ==========
_`.lw-frame.frameptr`: The ``frameptr`` slot holds the following values
for each state:
========== ============================================
Valid ``NULL``
PopPending Frame handle for most recently popped frame.
Disabled ``NULL``
========== ============================================
_`.lw-frame.lwPopPending`: The ``lwPopPending`` slot holds the
following values for each state:
========== =========
Valid ``FALSE``
PopPending ``TRUE``
Disabled ``FALSE``
========== =========
_`.lw-frame.state-for-gc`: It is not necessary for the tracer, format
code, pool, or any other part of the GC support in MPS to read either
of the two additional AP fields in order to scan a segment which
supports a lightweight allocation frame.
Synchronization
...............
@ -414,61 +341,41 @@ operation on an AP may only be performed by a single mutator thread at
a time. Each of the operations on allocation frames counts as an
operation on an AP.
_`.lw-frame.sync.pool`: Pools are permitted to read or modify the
lightweight frame state of an AP only in response to an operation on
that AP.
_`.lw-frame.sync.external`: The external functions
``mps_ap_frame_push()`` and ``mps_ap_frame_pop()`` are permitted to
read the values of the ``enabled`` and ``frameptr`` fields for the
supplied AP without claiming the arena lock. They are permitted to
modify the ``frameptr`` field if and only if ``enabled == FALSE``.
_`.lw-frame.sync.trip`: When a buffer trip happens, and the trap
wasn't set by MPS itself (that is, it wasn't because of a flip or for
logging), then the buffer code must check whether the AP has state
PopPending. If it does, the buffer code must call the Pool.
Implementation
..............
_`.lw-frame.push`: The external ``PushFrame()`` operation
_`.lw-frame.push`: The external *FramePush* operation
``mps_ap_frame_push()`` performs the following operations::
IF !APIsTrapped(ap)
AND StateOfFrame(ap) == Valid
AND ap->init == ap->alloc
AND ap->init < ap->limit
THEN
IF ap->init != ap->alloc
FAIL
ELSE IF ap->init < ap->limit
*frame_o = ap->init;
ELSE
WITH_ARENA_LOCK
PerformInternalPushFrameOperation(...)
PerformInternalFramePushOperation(...)
END
END
_`.lw-frame.push.limit`: The reason for not using the lightweight
operation when ``ap->init == ap->limit`` is that a frame pointer at
the limit of a buffer (and possibly therefore of a segment) would be
ambiguous: is it at the limit of the segment, or at the base of the
_`.lw-frame.push.limit`: The reason for testing ``ap->init <
ap->limit`` and not ``ap->init <= ap->limit`` is that a frame pointer
at the limit of a buffer (and possibly therefore of a segment) would
be ambiguous: is it at the limit of the segment, or at the base of the
segment that's adjacent in memory? The internal operation must handle
this case, for example by refilling the buffer and setting the frame
at the beginning.
_`.lw-frame.pop`: The external ``PopFrame()`` operation
_`.lw-frame.pop`: The external *FramePop* operation
(``mps_ap_frame_pop()``) performs the following operations::
IF StateOfFrame(ap) != Disabled
AND BufferBase(ap) <= frame
AND frame < ap->init
THEN
TrapAP(ap); /* ensure next allocation or push involves the pool */
ap->frameptr = frame;
ap->lwpopPending = TRUE;
IF ap->init != ap->alloc
FAIL
ELSE IF BufferBase(ap) <= frame AND frame < ap->init
ap->init = ap->alloc = frame;
ELSE
WITH_ARENA_LOCK
PerformInternalPopFrameOperation(...)
PerformInternalFramePopOperation(...)
END
END