1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-04-11 16:52:40 -07:00
emacs/mps/src/buffer.c
Richard Brooksby 39de4f1fa1 Merging mmdevel_action2
Copied from Perforce
 Change: 17498
 ServerID: perforce.ravenbrook.com
1997-04-15 15:24:00 +01:00

466 lines
12 KiB
C

/* impl.c.buffer: ALLOCATION BUFFER IMPLEMENTATION
*
* $HopeName: MMsrc!buffer.c(MMdevel_action2.4) $
* 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.
*
* Declarations for functions defined here are in impl.h.mpm.
*
* Declarations for the relevant structures are in impl.h.mpmst.
*
* 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(MMdevel_action2.4) $");
/* BufferCheck
*
* See design.mps.buffer.method.check for design.
* See impl.h.mpmst for structure definition.
*/
Bool BufferCheck(Buffer buffer)
{
CHECKS(Buffer, buffer);
CHECKL(buffer->serial < buffer->pool->bufferSerial); /* .trans.mod */
CHECKU(Space, buffer->space);
CHECKU(Pool, buffer->pool);
CHECKL(RankSetCheck(buffer->rankSet));
if(buffer->seg != NULL) {
CHECKL(SegCheck(buffer->seg)); /* design.mps.check.type.no-sig */
CHECKL(buffer->seg->buffer == buffer);
CHECKL(buffer->rankSet == buffer->seg->rankSet);
}
CHECKL(buffer->base <= buffer->apStruct.init);
CHECKL(buffer->apStruct.init <= buffer->apStruct.alloc);
CHECKL(buffer->apStruct.alloc <= buffer->apStruct.limit ||
buffer->apStruct.limit == 0);
CHECKL(buffer->alignment == buffer->pool->alignment);
CHECKL(AlignCheck(buffer->alignment));
CHECKL(AddrIsAligned(buffer->base, buffer->alignment));
CHECKL(AddrIsAligned(buffer->apStruct.init, buffer->alignment));
CHECKL(AddrIsAligned(buffer->apStruct.alloc, buffer->alignment));
CHECKL(AddrIsAligned(buffer->apStruct.limit, buffer->alignment));
CHECKL(RingCheck(&buffer->poolRing)); /* design.mps.check.type.no-sig */
/* buffer->p, and buffer->i are arbitrary and cannot be checked */
return TRUE;
}
/* BufferCreate -- create an allocation buffer
*
* design.mps.buffer.method.create
*/
Res BufferCreate(Buffer *bufferReturn, Pool pool, Rank rank)
{
Res res;
Buffer buffer;
Space space;
void *p;
AVER(bufferReturn != NULL);
AVERT(Pool, pool);
AVER(RankCheck(rank));
space = PoolSpace(pool);
/* Allocate the buffer structure. */
res = SpaceAlloc(&p, space, sizeof(BufferStruct));
if(res != ResOK) goto failAlloc;
buffer = p;
res = BufferInit(buffer, pool, rank);
if(res != ResOK) goto failInit;
*bufferReturn = buffer;
return ResOK;
failInit:
SpaceFree(space, (Addr)buffer, sizeof(BufferStruct));
failAlloc:
return res;
}
/* BufferInit -- initialize an allocation buffer */
Res BufferInit(Buffer buffer, Pool pool, Rank rank)
{
Res res;
AVER(buffer != NULL);
AVERT(Pool, pool);
/* The PoolClass should support buffer protocols */
AVER((pool->class->attr & AttrBUF)); /* .trans.mod */
AVER(RankCheck(rank));
/* Initialize the buffer. See impl.h.mpmst for a definition of the */
/* structure. sig and serial comes later .init.sig-serial */
buffer->space = PoolSpace(pool);
buffer->pool = pool;
buffer->seg = NULL;
buffer->rankSet = RankSetSingle(rank);
buffer->base = (Addr)0;
buffer->apStruct.init = (Addr)0;
buffer->apStruct.alloc = (Addr)0;
buffer->apStruct.limit = (Addr)0;
buffer->alignment = pool->alignment; /* .trans.mod */
RingInit(&buffer->poolRing);
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);
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;
}
/* BufferDestroy -- destroy an allocation buffer
*
* design.mps.buffer.method.destroy
*/
void BufferDestroy(Buffer buffer)
{
Space space;
AVERT(Buffer, buffer);
space = buffer->space;
BufferFinish(buffer);
SpaceFree(space, (Addr)buffer, sizeof(BufferStruct));
}
/* BufferFinish -- finish an allocation buffer */
void BufferFinish(Buffer buffer)
{
Pool pool;
AVERT(Buffer, buffer);
pool = buffer->pool;
/* The PoolClass should support buffer protocols */
AVER((pool->class->attr & AttrBUF)); /* .trans.mod */
AVER(BufferIsReady(buffer));
/* Detach the buffer from its owning pool. */
RingRemove(&buffer->poolRing);
/* Dispatch to the pool class method to finish the buffer. */
(*pool->class->bufferFinish)(pool, buffer);
/* Pool should make sure buffer is not attached. */
/* @@@@ Are there other conditions? */
AVER(BufferIsReset(buffer));
/* Unsign the finished buffer. */
buffer->sig = SigInvalid;
/* Finish off the generic buffer fields. */
RingFinish(&buffer->poolRing);
}
/* BufferSet/Reset -- set/reset a buffer
*
* Set sets the buffer base, init, alloc, and limit fields so that
* the buffer is ready to start allocating in area of memory. The
* alloc field is a copy of the init field.
*
* Reset sets the seg, base, init, alloc, and limit fields to
* zero, so that the next reserve request will call the fill
* method.
*/
void BufferSet(Buffer buffer, Seg seg, Addr base, Addr init, Addr limit)
{
AVERT(Buffer, buffer);
AVER(BufferIsReady(buffer));
AVER(SegCheck(seg));
AVER(seg->buffer == NULL);
AVER(SegBase(buffer->space, seg) <= base);
AVER(base <= init);
AVER(limit == 0 || init <= limit);
AVER(limit == 0 || limit <= SegLimit(buffer->space, seg));
buffer->seg = seg;
seg->buffer = buffer;
buffer->base = base;
buffer->apStruct.init = init;
buffer->apStruct.alloc = init;
buffer->apStruct.limit = limit;
}
void BufferReset(Buffer buffer)
{
AVERT(Buffer, buffer);
AVER(BufferIsReady(buffer));
AVER(buffer->seg->buffer == buffer);
buffer->seg->buffer = NULL;
buffer->seg = NULL;
buffer->base = (Addr)0;
buffer->apStruct.init = (Addr)0;
buffer->apStruct.alloc = (Addr)0;
buffer->apStruct.limit = (Addr)0;
}
/* Buffer Information
*
* BufferIsReset returns TRUE if and only if the buffer is in the
* reset state, i.e. with base, init, alloc, and limit set to zero.
*
* BufferIsReady returns TRUE iff 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.
*
* BufferAP returns the APStruct substructure of a buffer.
*
* BufferOfAP is a thread-safe (design.mps.interface.c.thread-safety)
* method of getting the buffer which owns an APStruct.
*
* BufferSpace is a thread-safe (design.mps.interface.c.thread-safety)
* method of getting the space which owns a buffer.
*
* BufferPool returns the pool to which a buffer is attached.
*/
Bool BufferIsReset(Buffer buffer)
{
AVERT(Buffer, buffer);
if(buffer->seg == NULL &&
buffer->base == (Addr)0 &&
buffer->apStruct.init == (Addr)0 &&
buffer->apStruct.alloc == (Addr)0 &&
buffer->apStruct.limit == (Addr)0)
return TRUE;
return FALSE;
}
Bool BufferIsReady(Buffer buffer)
{
AVERT(Buffer, buffer);
if(buffer->apStruct.init == buffer->apStruct.alloc)
return TRUE;
return FALSE;
}
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)
{
/* .design.mps.misc.parent.thread-safe */
return PARENT(BufferStruct, apStruct, ap);
}
/* design.mps.buffer.method.space */
/* This method must be thread-safe. See */
/* design.mps.interface.c.thread-safety. */
Space BufferSpace(Buffer buffer)
{
return buffer->space;
}
Pool (BufferPool)(Buffer buffer)
{
AVERT(Buffer, buffer);
return buffer->pool;
}
/* 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, fall through to the */
/* pool-specific allocation method. */
return BufferFill(pReturn, buffer, size);
}
/* BufferFill -- refill an empty buffer
*/
Res BufferFill(Addr *pReturn, Buffer buffer, Size size)
{
Res res;
Pool pool;
AVER(pReturn != NULL);
AVERT(Buffer, buffer);
AVER(size > 0);
AVER(SizeIsAligned(size, BufferPool(buffer)->alignment));
AVER(BufferIsReady(buffer));
pool = BufferPool(buffer);
res = (*pool->class->bufferFill)(pReturn, pool, buffer, size);
AVERT(Buffer, buffer);
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));
/* Buffer is "busy" */
AVER(!BufferIsReady(buffer));
/* See design.mps.collection.flip.
* If a flip occurs before this point, when the pool reads
* buffer->init it will point below the object, so it will be trashed
* and the commit must fail when trip is called. The pool will also
* read p (during the call to trip) which points to the invalid
* object at init.
*/
AVER(p == buffer->apStruct.init);
AVER(AddrAdd(buffer->apStruct.init, size) == buffer->apStruct.alloc);
/* 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). */
/* If a flip occurs at this point, the pool will see init */
/* 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. */
/* 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 tripped buffer
*/
Bool BufferTrip(Buffer buffer, Addr p, Size size)
{
Pool pool;
AVERT(Buffer, buffer);
AVER(p == buffer->apStruct.init);
AVER(size > 0);
AVER(SizeIsAligned(size, buffer->alignment));
AVER(AddrAdd(buffer->apStruct.init, size) == buffer->apStruct.alloc);
pool = BufferPool(buffer);
return (*pool->class->bufferTrip)(pool, buffer, p, size);
}
/* 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,
" Space $P\n", (WriteFP)buffer->space,
" Pool $P\n", (WriteFP)buffer->pool,
" Seg $P\n", (WriteFP)buffer->seg,
" rankSet $U\n", (WriteFU)buffer->rankSet,
" base $A init $A alloc $A limit $A\n",
buffer->base, buffer->apStruct.init,
buffer->apStruct.alloc, buffer->apStruct.limit,
" alignment $W\n", (WriteFW)buffer->alignment,
" p $P i $U\n", buffer->p, (WriteFU)buffer->i,
"} Buffer $P ($U)\n", (WriteFP)buffer, (WriteFU)buffer->serial,
NULL);
return res;
}