1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-17 00:30:37 -08:00
emacs/mps/src/buffer.c
David Jones b23483b22b Fixing bug in bufferdetach in calculation of spare
Copied from Perforce
 Change: 19169
 ServerID: perforce.ravenbrook.com
1998-01-08 11:24:52 +00:00

703 lines
20 KiB
C

/* impl.c.buffer: ALLOCATION BUFFER IMPLEMENTATION
*
* $HopeName: MMsrc!buffer.c(trunk.37) $
* Copyright (C) 1997 The Harlequin Group Limited. All rights reserved.
*
* This is (part of) the implementation of allocation buffers.
*
* Several macros which also form part of the implementation are
* in impl.h.mps.
*
* Several macros forming part of impl.h.mps should be consistent
* with the macros and functions in this module.
*
* DESIGN
*
* See design.mps.buffer.
*
* TRANSGRESSIONS
*
* .trans.mod: There are several instances where pool structures are
* directly accessed by this module (because impl.c.pool does not
* provide an adequate (or adequately documented) interface. They
* bear this tag.
*/
#include "mpm.h"
SRCID(buffer, "$HopeName: MMsrc!buffer.c(trunk.37) $");
/* BufferCheck -- check consistency of a buffer */
Bool BufferCheck(Buffer buffer)
{
CHECKS(Buffer, buffer);
CHECKL(buffer->serial < buffer->pool->bufferSerial); /* .trans.mod */
CHECKU(Arena, buffer->arena);
CHECKU(Pool, buffer->pool);
CHECKL(buffer->arena == buffer->pool->arena);
CHECKL(RingCheck(&buffer->poolRing)); /* design.mps.check.type.no-sig */
CHECKL(BoolCheck(buffer->isMutator));
CHECKL(buffer->fillSize >= 0.0);
CHECKL(buffer->emptySize >= 0.0);
CHECKL(RankSetCheck(buffer->rankSet));
CHECKL(buffer->alignment == buffer->pool->alignment);
CHECKL(AlignCheck(buffer->alignment));
/* If any of the buffer's fields indicate that it is reset, make */
/* sure it is really reset. Otherwise, check various properties */
/* of the non-reset fields. */
if(buffer->seg == NULL ||
buffer->base == (Addr)0 ||
buffer->apStruct.init == (Addr)0 ||
buffer->apStruct.alloc == (Addr)0 ||
buffer->poolLimit == (Addr)0) {
CHECKL(buffer->seg == NULL);
CHECKL(buffer->base == (Addr)0);
CHECKL(buffer->initAtFlip == (Addr)0);
CHECKL(buffer->apStruct.init == (Addr)0);
CHECKL(buffer->apStruct.alloc == (Addr)0);
CHECKL(buffer->apStruct.limit == (Addr)0);
CHECKL(buffer->poolLimit == (Addr)0);
} else {
/* The buffer is attached to a segment. Make sure its fields */
/* tally with those of the segment. */
CHECKL(SegCheck(buffer->seg)); /* design.mps.check.type.no-sig */
CHECKL(SegBuffer(buffer->seg) == buffer);
CHECKL(SegPool(buffer->seg) == buffer->pool);
CHECKL(buffer->rankSet == SegRankSet(buffer->seg));
/* These fields should obey the ordering */
/* base <= init <= alloc <= poolLimit */
CHECKL(buffer->base <= buffer->apStruct.init);
CHECKL(buffer->apStruct.init <= buffer->apStruct.alloc);
CHECKL(buffer->apStruct.alloc <= buffer->poolLimit);
/* Check that the fields are aligned to the buffer alignment. */
CHECKL(AddrIsAligned(buffer->base, buffer->alignment));
CHECKL(AddrIsAligned(buffer->initAtFlip, buffer->alignment));
CHECKL(AddrIsAligned(buffer->apStruct.init, buffer->alignment));
CHECKL(AddrIsAligned(buffer->apStruct.alloc, buffer->alignment));
CHECKL(AddrIsAligned(buffer->apStruct.limit, buffer->alignment));
CHECKL(AddrIsAligned(buffer->poolLimit, buffer->alignment));
/* 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 */
/* to "alloc" (.commit.after). Also, when the buffer is */
/* trapped, initAtFlip should hold the init at flip, which is */
/* between the base and current init. Otherwise, initAtFlip */
/* is kept at zero to avoid misuse (see */
/* request.dylan.170429.sol.zero). */
if(buffer->apStruct.limit != (Addr)0) {
CHECKL(buffer->apStruct.limit == buffer->poolLimit);
CHECKL(buffer->initAtFlip == (Addr)0);
} else {
CHECKL(buffer->apStruct.init == buffer->initAtFlip ||
buffer->apStruct.init == buffer->apStruct.alloc);
CHECKL(buffer->base <= buffer->initAtFlip);
CHECKL(buffer->initAtFlip <= buffer->apStruct.init);
/* Only buffers which allocate pointers get trapped. */
CHECKL(buffer->rankSet != RankSetEMPTY);
}
}
/* buffer->p, and buffer->i are arbitrary values determined by the */
/* owning pool and cannot be checked */
return TRUE;
}
/* BufferDescribe -- write out description of buffer
*
* See impl.h.mpmst for structure definitions.
*/
Res BufferDescribe(Buffer buffer, mps_lib_FILE *stream)
{
int res;
AVERT(Buffer, buffer);
AVER(stream != NULL);
res = WriteF(stream,
"Buffer $P ($U) {\n",
(WriteFP)buffer, (WriteFU)buffer->serial,
" Arena $P\n", (WriteFP)buffer->arena,
" Pool $P\n", (WriteFP)buffer->pool,
buffer->isMutator ?
" Mutator Buffer\n" : " Internal Buffer\n",
" fillSize $UKb\n", (WriteFU)(buffer->fillSize / 1024),
" emptySize $UKb\n", (WriteFU)(buffer->emptySize / 1024),
" Seg $P\n", (WriteFP)buffer->seg,
" rankSet $U\n", (WriteFU)buffer->rankSet,
" alignment $W\n", (WriteFW)buffer->alignment,
" base $A\n", buffer->base,
" initAtFlip $A\n", buffer->initAtFlip,
" init $A\n", buffer->apStruct.init,
" alloc $A\n", buffer->apStruct.alloc,
" limit $A\n", buffer->apStruct.limit,
" poolLimit $A\n", buffer->poolLimit,
" p $P i $U\n", buffer->p, (WriteFU)buffer->i,
"} Buffer $P ($U)\n",
(WriteFP)buffer, (WriteFU)buffer->serial,
NULL);
return res;
}
/* BufferInitV -- initialize an allocation buffer */
static Res BufferInitV(Buffer buffer, Pool pool, Bool isMutator, va_list args)
{
Res res;
AVER(buffer != NULL);
AVERT(Pool, pool);
/* The PoolClass should support buffer protocols */
AVER((pool->class->attr & AttrBUF)); /* .trans.mod */
/* Initialize the buffer. See impl.h.mpmst for a definition of */
/* the structure. sig and serial comes later .init.sig-serial */
buffer->arena = PoolArena(pool);
buffer->pool = pool;
RingInit(&buffer->poolRing);
buffer->isMutator = isMutator;
buffer->fillSize = 0.0;
buffer->emptySize = 0.0;
buffer->alignment = pool->alignment; /* .trans.mod */
buffer->seg = NULL;
buffer->rankSet = RankSetEMPTY;
buffer->base = (Addr)0;
buffer->initAtFlip = (Addr)0;
buffer->apStruct.init = (Addr)0;
buffer->apStruct.alloc = (Addr)0;
buffer->apStruct.limit = (Addr)0;
buffer->poolLimit = (Addr)0;
buffer->p = NULL;
buffer->i = 0;
/* Dispatch to the pool class method to perform any extra */
/* initialization of the buffer. */
res = (*pool->class->bufferInit)(pool, buffer, args);
if(res != ResOK)
return res;
/* .init.sig-serial: Now that it's initialized, sign the buffer, */
/* give it a serial number, and check it. */
buffer->sig = BufferSig;
buffer->serial = pool->bufferSerial; /* .trans.mod */
++pool->bufferSerial;
AVERT(Buffer, buffer);
/* Attach the initialized buffer to the pool. */
RingAppend(&pool->bufferRing, &buffer->poolRing);
return ResOK;
}
/* BufferCreate -- create an allocation buffer
*
* See design.mps.buffer.method.create.
*/
Res BufferCreate(Buffer *bufferReturn, Pool pool, ...)
{
Res res;
va_list args;
va_start(args, pool);
res = BufferCreateV(bufferReturn, pool, FALSE, args);
va_end(args);
return res;
}
/* BufferCreateV -- create an allocation buffer, with varargs
*
* See design.mps.buffer.method.create.
*/
Res BufferCreateV(Buffer *bufferReturn,
Pool pool, Bool isMutator, va_list args)
{
Res res;
Buffer buffer;
Arena arena;
void *p;
AVER(bufferReturn != NULL);
AVERT(Pool, pool);
arena = PoolArena(pool);
/* Allocate memory for the buffer descriptor structure. */
res = ArenaAlloc(&p, arena, sizeof(BufferStruct));
if(res != ResOK) goto failAlloc;
buffer = p;
/* Initialize the buffer descriptor structure. */
res = BufferInitV(buffer, pool, isMutator, args);
if(res != ResOK) goto failInit;
*bufferReturn = buffer;
return ResOK;
failInit:
ArenaFree(arena, buffer, sizeof(BufferStruct));
failAlloc:
return res;
}
/* BufferDetach -- detach a buffer from a segment */
void BufferDetach(Buffer buffer, Pool pool)
{
AVERT(Buffer, buffer);
AVER(BufferIsReady(buffer));
if(!BufferIsReset(buffer)) {
Size spare;
/* 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);
spare = AddrOffset(buffer->apStruct.alloc,
buffer->poolLimit);
buffer->emptySize += spare;
if(buffer->isMutator) {
buffer->pool->emptyMutatorSize += spare;
buffer->arena->emptyMutatorSize += spare;
buffer->arena->allocMutatorSize += AddrOffset(buffer->base,
buffer->apStruct.alloc);
} else {
buffer->pool->emptyInternalSize += spare;
buffer->arena->emptyInternalSize += spare;
}
/* Reset the buffer. */
SegSetBuffer(buffer->seg, NULL);
buffer->seg = NULL;
buffer->base = (Addr)0;
buffer->initAtFlip = (Addr)0;
buffer->apStruct.init = (Addr)0;
buffer->apStruct.alloc = (Addr)0;
buffer->apStruct.limit = (Addr)0;
buffer->poolLimit = (Addr)0;
}
}
/* BufferDestroy -- destroy an allocation buffer
*
* design.mps.buffer.method.destroy
*/
void BufferDestroy(Buffer buffer)
{
Arena arena;
AVERT(Buffer, buffer);
arena = buffer->arena;
BufferFinish(buffer);
ArenaFree(arena, buffer, sizeof(BufferStruct));
}
/* BufferFinish -- finish an allocation buffer */
void BufferFinish(Buffer buffer)
{
Pool pool;
AVERT(Buffer, buffer);
pool = BufferPool(buffer);
/* The PoolClass should support buffer protocols */
AVER((pool->class->attr & AttrBUF)); /* .trans.mod */
AVER(BufferIsReady(buffer));
BufferDetach(buffer, pool);
/* Ask the pool to do any pool-specific finishing. */
(*pool->class->bufferFinish)(pool, buffer);
/* Detach the buffer from its owning pool and unsig it. */
RingRemove(&buffer->poolRing);
buffer->sig = SigInvalid;
/* Finish off the generic buffer fields. */
RingFinish(&buffer->poolRing);
}
/* BufferIsReset -- test whether a buffer is in the "reset" state
*
* A buffer is "reset" when it is not attached to a segment. In this
* state all of the pointers into the segment are zero. This condition
* is checked by BufferCheck.
*/
Bool BufferIsReset(Buffer buffer)
{
AVERT(Buffer, buffer);
if(buffer->seg == NULL)
return TRUE;
return FALSE;
}
/* BufferIsReady -- test whether a buffer is ready for reserve
*
* BufferIsReady returns TRUE if and only if the buffer is not between
* a reserve and commit. The result is only reliable if the client is
* not currently using the buffer, since it may update the alloc and
* init pointers asynchronously.
*/
Bool BufferIsReady(Buffer buffer)
{
AVERT(Buffer, buffer);
if(buffer->apStruct.init == buffer->apStruct.alloc)
return TRUE;
return FALSE;
}
/* BufferIsMutator
*
* returns TRUE iff mutator was created at mutator request (ie a
* mutator buffer).
*/
Bool BufferIsMutator(Buffer buffer)
{
AVERT(Buffer, buffer);
return buffer->isMutator;
}
/* BufferReserve -- reserve memory from an allocation buffer
*
* .reserve: Keep in sync with impl.h.mps.reserve.
*/
Res BufferReserve(Addr *pReturn, Buffer buffer, Size size)
{
Addr next;
AVER(pReturn != NULL);
AVERT(Buffer, buffer);
AVER(size > 0);
AVER(SizeIsAligned(size, BufferPool(buffer)->alignment));
AVER(BufferIsReady(buffer));
/* Is there enough room in the unallocated portion of the buffer to */
/* satisfy the request? If so, just increase the alloc marker and */
/* return a pointer to the area below it. */
next = AddrAdd(buffer->apStruct.alloc, size);
if(next > buffer->apStruct.alloc && next <= buffer->apStruct.limit) {
buffer->apStruct.alloc = next;
*pReturn = buffer->apStruct.init;
return ResOK;
}
/* If the buffer can't accommodate the request, call "fill". */
return BufferFill(pReturn, buffer, size);
}
/* BufferFill -- refill an empty buffer
*
* BufferFill is entered by the "reserve" operation on a buffer if
* there isn't enough room between "alloc" and "limit" to satisfy
* an allocation request. This might be because the buffer has been
* trapped and "limit" has been set to zero.
*/
Res BufferFill(Addr *pReturn, Buffer buffer, Size size)
{
Res res;
Pool pool;
Seg seg;
Addr base, limit, next;
Size filled;
AVER(pReturn != NULL);
AVERT(Buffer, buffer);
AVER(size > 0);
AVER(SizeIsAligned(size, BufferPool(buffer)->alignment));
AVER(BufferIsReady(buffer));
pool = BufferPool(buffer);
/* If we're here because the buffer was trapped, then the mutator */
/* must not have been between reserve and commit when flip */
/* happened. In that case, there's no need to invalidate any */
/* object or fail to reserve (provided there's enough space in */
/* the buffer). Untrap the buffer, and try again. */
if(!BufferIsReset(buffer) && buffer->apStruct.limit == (Addr)0) {
buffer->apStruct.limit = buffer->poolLimit;
buffer->initAtFlip = (Addr)0;
next = AddrAdd(buffer->apStruct.alloc, size);
if(next > buffer->apStruct.alloc &&
next <= buffer->apStruct.limit) {
buffer->apStruct.alloc = next;
*pReturn = buffer->apStruct.init;
return ResOK;
}
}
/* There really isn't enough room for the allocation now. */
AVER(AddrAdd(buffer->apStruct.alloc, size) > buffer->apStruct.limit ||
AddrAdd(buffer->apStruct.alloc, size) < buffer->apStruct.alloc);
BufferDetach(buffer, pool);
/* Ask the pool for a segment and some memory. */
res = (*pool->class->bufferFill)(&seg, &base, &limit,
pool, buffer, size);
if(res != ResOK)
return res;
AVER(SegCheck(seg));
AVER(SegBuffer(seg) == NULL);
AVER(SegBase(seg) <= base);
AVER(AddrAdd(base, size) <= limit);
AVER(limit <= SegLimit(seg));
/* Set up the buffer to point at the memory given by the pool */
/* and do the allocation that was requested by the client. */
buffer->seg = seg;
SegSetBuffer(seg, buffer);
buffer->base = base;
buffer->apStruct.init = base;
buffer->apStruct.alloc = AddrAdd(base, size);
buffer->apStruct.limit = limit;
AVER(buffer->initAtFlip == (Addr)0);
buffer->poolLimit = limit;
filled = AddrOffset(base, limit);
buffer->fillSize += filled;
if(buffer->isMutator) {
buffer->pool->fillMutatorSize += filled;
buffer->arena->fillMutatorSize += filled;
} else {
buffer->pool->fillInternalSize += filled;
buffer->arena->fillInternalSize += filled;
}
AVERT(Buffer, buffer);
*pReturn = base;
return res;
}
/* BufferCommit -- commit memory previously reserved
*
* .commit: Keep in sync with impl.h.mps.commit.
*/
Bool BufferCommit(Buffer buffer, Addr p, Size size)
{
AVERT(Buffer, buffer);
AVER(size > 0);
AVER(SizeIsAligned(size, BufferPool(buffer)->alignment));
AVER(!BufferIsReady(buffer));
/* See design.mps.collection.flip. */
/* .commit.before: If a flip occurs before this point, when the */
/* pool reads "initAtFlip" it will point below the object, so it */
/* will be trashed and the commit must fail when trip is called. */
AVER(p == buffer->apStruct.init);
AVER(AddrAdd(buffer->apStruct.init, size) == buffer->apStruct.alloc);
/* .commit.update: Atomically update the init pointer to declare */
/* that the object is initialized (though it may be invalid if a */
/* flip occurred). */
buffer->apStruct.init = buffer->apStruct.alloc;
/* .improve.memory-barrier: Memory barrier here on the DEC Alpha */
/* (and other relaxed memory order architectures). */
/* .commit.after: If a flip occurs at this point, the pool will */
/* see "initAtFlip" above the object, which is valid, so it will */
/* be collected. The commit must succeed when trip is called. */
/* The pointer "p" will have been fixed up. (@@@@ Will it?) */
/* .commit.trip: Trip the buffer if a flip has occurred. */
if(buffer->apStruct.limit == 0)
return BufferTrip(buffer, p, size);
/* No flip occurred, so succeed. */
return TRUE;
}
/* BufferTrip -- act on a trapped buffer */
Bool BufferTrip(Buffer buffer, Addr p, Size size)
{
Pool pool;
AVERT(Buffer, buffer);
AVER(p != 0);
AVER(size > 0);
AVER(SizeIsAligned(size, buffer->alignment));
/* The limit field should be zero, because that's how trip gets */
/* called. See .commit.trip. */
AVER(buffer->apStruct.limit == 0);
/* The init and alloc fields should be equal at this point, because */
/* the step .commit.update has happened. */
AVER(buffer->apStruct.init == buffer->apStruct.alloc);
/* The p parameter points at the base address of the allocated */
/* block, the end of which should now coincide with the init and */
/* alloc fields. */
AVER(AddrAdd(p, size) == buffer->apStruct.init);
pool = BufferPool(buffer);
AVER(PoolHasAddr(pool, p));
AVER(SegPool(BufferSeg(buffer)) == pool);
/* .trip.untrap: If the flip occurred before commit set "init" */
/* to "alloc" (see .commit.before) then the object is invalid */
/* (won't've been scanned) so undo the allocation and fail commit. */
/* Otherwise (see .commit.after) the object is valid (will've been */
/* scanned) so commit can simply succeed. */
if(buffer->apStruct.init == buffer->initAtFlip)
return TRUE;
else {
buffer->apStruct.init = p;
buffer->apStruct.alloc = p;
buffer->apStruct.limit = buffer->poolLimit;
buffer->initAtFlip = (Addr)0;
return FALSE;
}
}
/* BufferFlip -- trap buffer at GC flip time
*
* .flip: Tells the buffer that a flip has occurred. If the buffer is
* between reserve and commit, and has a rank (i.e. references),
* and has the two-phase protocol, then the object being initialized
* is invalidated by failing the next commit. The buffer code handles
* this automatically. If the buffer is reset there is no effect,
* since there is no object to invalidate. If the buffer is already
* flipped (i.e. "limit" is zero) there is no effect, since the
* object is already invalid by a previous trace. The buffer becomes
* unflipped at the next reserve or commit operation. This is handled
* by BufferFill (.fill.untrap) or BufferTrip (.trip.untrap).
*/
void BufferFlip(Buffer buffer)
{
AVERT(Buffer, buffer);
if(buffer->rankSet != RankSetEMPTY &&
buffer->apStruct.limit != (Addr)0) {
AVER(buffer->initAtFlip == (Addr)0);
buffer->initAtFlip = buffer->apStruct.init;
buffer->apStruct.limit = (Addr)0;
}
}
/* BufferScanLimit -- return limit of data to which to scan
*
* Returns the highest address to which it is safe to scan objects
* in the buffer. When the buffer is not flipped, this is the
* "init" of the AP. When the buffer is flipped, it is the value
* that "init" had at flip time. [Could make BufferScanLimit
* return the AP "alloc" when using ambiguous scanning.]
*/
Addr BufferScanLimit(Buffer buffer)
{
if(buffer->apStruct.limit != (Addr)0)
return buffer->apStruct.init;
else
return buffer->initAtFlip;
}
AP (BufferAP)(Buffer buffer)
{
AVERT(Buffer, buffer);
return BufferAP(buffer);
}
/* design.mps.buffer.method.ofap */
/* This method must be thread-safe. See */
/* design.mps.interface.c.thread-safety. */
Buffer (BufferOfAP)(AP ap)
{
/* Can't AVER ap as that would not be thread safe */
/* No Check method for AP, so no AVER */
/* .design.mps.misc.parent.thread-safe */
return BufferOfAP(ap);
}
/* design.mps.buffer.method.space */
/* This method must be thread-safe. See */
/* design.mps.interface.c.thread-safety. */
Arena (BufferArena)(Buffer buffer)
{
/* Can't AVER buffer as that would not be thread-safe. */
/* AVERT(Buffer, buffer); */
return BufferArena(buffer);
}
Pool (BufferPool)(Buffer buffer)
{
AVERT(Buffer, buffer);
return BufferPool(buffer);
}
Seg (BufferSeg)(Buffer buffer)
{
AVERT(Buffer, buffer);
return BufferSeg(buffer);
}
RankSet (BufferRankSet)(Buffer buffer)
{
AVERT(Buffer, buffer);
return BufferRankSet(buffer);
}
Addr (BufferBase)(Buffer buffer)
{
AVERT(Buffer, buffer);
return BufferBase(buffer);
}
Addr (BufferGetInit)(Buffer buffer)
{
AVERT(Buffer, buffer);
return BufferGetInit(buffer);
}
Addr (BufferAlloc)(Buffer buffer)
{
AVERT(Buffer, buffer);
return BufferAlloc(buffer);
}
Addr (BufferLimit)(Buffer buffer)
{
AVERT(Buffer, buffer);
return BufferLimit(buffer);
}