diff --git a/mps/code/abq.c b/mps/code/abq.c index e29a9786599..c327a721a54 100644 --- a/mps/code/abq.c +++ b/mps/code/abq.c @@ -1,18 +1,15 @@ -/* abq.c: AVAILABLE BLOCK QUEUE +/* abq.c: QUEUE IMPLEMENTATION * * $Id$ * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. * - * .readership: Any MPS developer + * .purpose: A fixed-length FIFO queue. * - * .purpose: A FIFO queue substrate for - * - * .design: See + * .design: */ #include "meter.h" #include "abq.h" -#include "cbs.h" #include "mpm.h" SRCID(abq, "$Id$"); @@ -20,37 +17,39 @@ SRCID(abq, "$Id$"); /* Private prototypes */ -static Size ABQQueueSize(Count elements); +static Size ABQQueueSize(Count elements, Size elementSize); static Index ABQNextIndex(ABQ abq, Index index); +static Addr ABQElement(ABQ abq, Index index); /* Methods */ /* ABQInit -- Initialize an ABQ * - * items is the number of items the queue can hold + * elements is the number of elements the queue can hold */ -Res ABQInit(Arena arena, ABQ abq, void *owner, Count items) +Res ABQInit(Arena arena, ABQ abq, void *owner, Count elements, Size elementSize) { - Count elements; void *p; Res res; AVERT(Arena, arena); AVER(abq != NULL); - AVER(items > 0); + AVER(elements > 0); - elements = items + 1; - - res = ControlAlloc(&p, arena, ABQQueueSize(elements), + /* Necessary in order to be able to distinguish "empty" from "full" */ + elements = elements + 1; + + res = ControlAlloc(&p, arena, ABQQueueSize(elements, elementSize), /* withReservoirPermit */ FALSE); if (res != ResOK) return res; abq->elements = elements; + abq->elementSize = elementSize; abq->in = 0; abq->out = 0; - abq->queue = (CBSBlock *)p; + abq->queue = p; METER_INIT(abq->push, "push", owner); METER_INIT(abq->pop, "pop", owner); @@ -67,19 +66,12 @@ Res ABQInit(Arena arena, ABQ abq, void *owner, Count items) /* ABQCheck -- validate an ABQ */ Bool ABQCheck(ABQ abq) { - Index index; - CHECKS(ABQ, abq); CHECKL(abq->elements > 0); + CHECKL(abq->elementSize > 0); CHECKL(abq->in < abq->elements); CHECKL(abq->out < abq->elements); CHECKL(abq->queue != NULL); - /* Is this really a local check? */ - for (index = abq->out; index != abq->in; ) { - CHECKL(CBSBlockCheck(abq->queue[index])); - if (++index == abq->elements) - index = 0; - } return TRUE; } @@ -95,7 +87,7 @@ void ABQFinish(Arena arena, ABQ abq) METER_EMIT(&abq->pop); METER_EMIT(&abq->peek); METER_EMIT(&abq->delete); - ControlFree(arena, abq->queue, ABQQueueSize(abq->elements)); + ControlFree(arena, abq->queue, ABQQueueSize(abq->elements, abq->elementSize)); abq->elements = 0; abq->queue = NULL; @@ -104,18 +96,17 @@ void ABQFinish(Arena arena, ABQ abq) } -/* ABQPush -- push a block onto the tail of the ABQ */ -Res ABQPush(ABQ abq, CBSBlock block) +/* ABQPush -- push an element onto the tail of the ABQ */ +Res ABQPush(ABQ abq, Addr element) { AVERT(ABQ, abq); - AVERT(CBSBlock, block); METER_ACC(abq->push, ABQDepth(abq)); if (ABQIsFull(abq)) return ResFAIL; - abq->queue[abq->in] = block; + mps_lib_memcpy(ABQElement(abq, abq->in), element, abq->elementSize); abq->in = ABQNextIndex(abq, abq->in); AVERT(ABQ, abq); @@ -123,10 +114,10 @@ Res ABQPush(ABQ abq, CBSBlock block) } -/* ABQPop -- pop a block from the head of the ABQ */ -Res ABQPop(ABQ abq, CBSBlock *blockReturn) +/* ABQPop -- pop an element from the head of the ABQ */ +Res ABQPop(ABQ abq, Addr elementReturn) { - AVER(blockReturn != NULL); + AVER(elementReturn != NULL); AVERT(ABQ, abq); METER_ACC(abq->pop, ABQDepth(abq)); @@ -134,8 +125,7 @@ Res ABQPop(ABQ abq, CBSBlock *blockReturn) if (ABQIsEmpty(abq)) return ResFAIL; - *blockReturn = abq->queue[abq->out]; - AVERT(CBSBlock, *blockReturn); + mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize); abq->out = ABQNextIndex(abq, abq->out); @@ -145,9 +135,9 @@ Res ABQPop(ABQ abq, CBSBlock *blockReturn) /* ABQPeek -- peek at the head of the ABQ */ -Res ABQPeek(ABQ abq, CBSBlock *blockReturn) +Res ABQPeek(ABQ abq, Addr elementReturn) { - AVER(blockReturn != NULL); + AVER(elementReturn != NULL); AVERT(ABQ, abq); METER_ACC(abq->peek, ABQDepth(abq)); @@ -155,8 +145,7 @@ Res ABQPeek(ABQ abq, CBSBlock *blockReturn) if (ABQIsEmpty(abq)) return ResFAIL; - *blockReturn = abq->queue[abq->out]; - AVERT(CBSBlock, *blockReturn); + mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize); /* Identical to pop, but don't increment out */ @@ -165,46 +154,49 @@ Res ABQPeek(ABQ abq, CBSBlock *blockReturn) } -/* ABQDelete -- delete a block from the ABQ */ -Res ABQDelete(ABQ abq, CBSBlock block) +typedef struct ABQDeleteClosureStruct *ABQDeleteClosure; +typedef struct ABQDeleteClosureStruct { + Addr element; + Size elementSize; + Res res; +} ABQDeleteClosureStruct; + + +static Res ABQDeleteCallback(ABQDisposition *dispositionReturn, Addr element, + void *closureP) { - Index index, next, in; - CBSBlock *queue; - - AVERT(ABQ, abq); - AVERT(CBSBlock, block); - - METER_ACC(abq->delete, ABQDepth(abq)); - - index = abq->out; - in = abq->in; - queue = abq->queue; - - while (index != in) { - if (queue[index] == block) { - goto found; - } - index = ABQNextIndex(abq, index); + ABQDeleteClosure closure = closureP; + if (mps_lib_memcmp(element, closure->element, closure->elementSize) == 0) { + *dispositionReturn = ABQDispositionDELETE; + closure->res = ResOK; + } else { + *dispositionReturn = ABQDispositionKEEP; } - - return ResFAIL; - -found: - /* index points to the node to be removed */ - next = ABQNextIndex(abq, index); - while (next != in) { - queue[index] = queue[next]; - index = next; - next = ABQNextIndex(abq, index); - } - abq->in = index; - AVERT(ABQ, abq); return ResOK; } +/* ABQDelete -- delete an element from the ABQ */ +Res ABQDelete(ABQ abq, Addr element) +{ + ABQDeleteClosureStruct closure; + + AVERT(ABQ, abq); + + METER_ACC(abq->delete, ABQDepth(abq)); + + closure.element = element; + closure.elementSize = abq->elementSize; + closure.res = ResFAIL; + + ABQIterate(abq, ABQDeleteCallback, &closure); + + return closure.res; +} + + /* ABQDescribe -- Describe an ABQ */ -Res ABQDescribe(ABQ abq, mps_lib_FILE *stream) +Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream) { Res res; Index index; @@ -224,11 +216,10 @@ Res ABQDescribe(ABQ abq, mps_lib_FILE *stream) return res; for (index = abq->out; index != abq->in; ) { - res = CBSBlockDescribe(abq->queue[index], stream); + res = (*describeElement)(ABQElement(abq, index), stream); if(res != ResOK) return res; - if (++index == abq->elements) - index = 0; + index = ABQNextIndex(abq, index); } res = WriteF(stream, "\n", NULL); @@ -274,7 +265,7 @@ Bool ABQIsFull(ABQ abq) } -/* ABQDepth -- return the number of items in an ABQ */ +/* ABQDepth -- return the number of elements in an ABQ */ Count ABQDepth(ABQ abq) { Index out, in; @@ -290,13 +281,65 @@ Count ABQDepth(ABQ abq) } -/* ABQQueueSize -- calculate the storage required for the vector to - store elements items */ -static Size ABQQueueSize(Count elements) +/* ABQDispositionCheck -- check method for an ABQDisposition value */ +static Bool ABQDispositionCheck(ABQDisposition disposition) { - /* strange but true: the sizeof expression calculates the size of a - single queue element */ - return (Size)(sizeof(((ABQ)NULL)->queue[0]) * elements); + CHECKL(disposition == ABQDispositionKEEP + || disposition == ABQDispositionDELETE); + UNUSED(disposition); /* */ + + return TRUE; +} + + +/* ABQIterate -- call 'iterate' for each element in an ABQ */ +void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP) +{ + Index copy, index, in; + Res res; + + AVERT(ABQ, abq); + AVER(FUNCHECK(iterate)); + + copy = abq->out; + index = abq->out; + in = abq->in; + + while (index != in) { + Addr element = ABQElement(abq, index); + ABQDisposition disposition = ABQDispositionNONE; + res = (*iterate)(&disposition, element, closureP); + AVERT(ABQDisposition, disposition); + if (disposition == ABQDispositionKEEP) { + if (copy != index) + mps_lib_memcpy(ABQElement(abq, copy), element, abq->elementSize); + copy = ABQNextIndex(abq, copy); + } + index = ABQNextIndex(abq, index); + if (res != ResOK) + break; + } + + /* If any elements were deleted, need to copy remainder of queue. */ + if (copy != index) { + while (index != in) { + mps_lib_memcpy(ABQElement(abq, copy), ABQElement(abq, index), + abq->elementSize); + copy = ABQNextIndex(abq, copy); + index = ABQNextIndex(abq, index); + } + abq->in = copy; + } + + AVERT(ABQ, abq); +} + + +/* ABQQueueSize -- calculate the storage required for the vector to + store the elements */ +static Size ABQQueueSize(Count elements, Size elementSize) +{ + return (Size)(elements * elementSize); } @@ -310,6 +353,12 @@ static Index ABQNextIndex(ABQ abq, Index index) return next; } +/* ABQElement -- return pointer to the index'th element in the queue + vector. */ +static Addr ABQElement(ABQ abq, Index index) { + return AddrAdd(abq->queue, index * abq->elementSize); +} + /* C. COPYRIGHT AND LICENSE * diff --git a/mps/code/abq.h b/mps/code/abq.h index e39635dfcaa..085b1ed3d66 100644 --- a/mps/code/abq.h +++ b/mps/code/abq.h @@ -1,18 +1,17 @@ -/* abq.h: ABQ INTERFACE +/* abq.h: QUEUE INTERFACE * * $Id$ * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. * - * .purpose: A FIFO queue substrate for + * .purpose: A fixed-length FIFO queue. * - * .source: + * .design: */ #ifndef abq_h #define abq_h #include "meter.h" -#include "cbs.h" #include "mpm.h" @@ -24,17 +23,22 @@ /* Prototypes */ typedef struct ABQStruct *ABQ; -extern Res ABQInit(Arena arena, ABQ abq, void *owner, Count items); +typedef Res (*ABQDescribeElement)(Addr element, mps_lib_FILE *stream); +typedef unsigned ABQDisposition; +typedef Res (*ABQIterateMethod)(ABQDisposition *dispositionReturn, Addr element, void *closureP); + +extern Res ABQInit(Arena arena, ABQ abq, void *owner, Count elements, Size elementSize); extern Bool ABQCheck(ABQ abq); extern void ABQFinish(Arena arena, ABQ abq); -extern Res ABQPush(ABQ abq, CBSBlock block); -extern Res ABQPop(ABQ abq, CBSBlock *blockReturn); -extern Res ABQPeek(ABQ abq, CBSBlock *blockReturn); -extern Res ABQDelete(ABQ abq, CBSBlock block); -extern Res ABQDescribe(ABQ abq, mps_lib_FILE *stream); +extern Res ABQPush(ABQ abq, Addr element); +extern Res ABQPop(ABQ abq, Addr elementReturn); +extern Res ABQPeek(ABQ abq, Addr elementReturn); +extern Res ABQDelete(ABQ abq, Addr element); +extern Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream); extern Bool ABQIsEmpty(ABQ abq); extern Bool ABQIsFull(ABQ abq); extern Count ABQDepth(ABQ abq); +extern void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP); /* Types */ @@ -42,9 +46,10 @@ extern Count ABQDepth(ABQ abq); typedef struct ABQStruct { Count elements; + Size elementSize; Index in; Index out; - CBSBlock *queue; + Addr queue; /* Meter queue depth at each operation */ METER_DECL(push); @@ -55,6 +60,12 @@ typedef struct ABQStruct Sig sig; } ABQStruct; +enum { + ABQDispositionKEEP = 1, /* keep item in queue */ + ABQDispositionDELETE, /* delete element from queue */ + ABQDispositionNONE /* no disposition (error) */ +}; + #endif /* abq_h */ diff --git a/mps/code/abqtest.c b/mps/code/abqtest.c index 7de0ee537a7..f1685cde4b7 100644 --- a/mps/code/abqtest.c +++ b/mps/code/abqtest.c @@ -41,54 +41,42 @@ static unsigned popee = 1; static unsigned deleted = 0; -typedef struct TestStruct *Test; +typedef struct TestBlockStruct *TestBlock; -typedef struct TestStruct +typedef struct TestBlockStruct { - Test next; + TestBlock next; unsigned id; - CBSBlockStruct cbsBlockStruct; -} TestStruct; + Addr base; + Addr limit; +} TestBlockStruct; -static CBSBlock TestCBSBlock(Test t) +static TestBlock testBlocks = NULL; + + +static TestBlock CreateTestBlock(unsigned no) { - return &t->cbsBlockStruct; -} - -static Test CBSBlockTest(CBSBlock c) -{ - return PARENT(TestStruct, cbsBlockStruct, c); -} - - -static Test testBlocks = NULL; - - -static CBSBlock CreateCBSBlock(unsigned no) -{ - Test b = malloc(sizeof(TestStruct)); + TestBlock b = malloc(sizeof(TestBlockStruct)); cdie(b != NULL, "malloc"); b->next = testBlocks; b->id = no; - b->cbsBlockStruct.base = 0; - b->cbsBlockStruct.limit = 0; + b->base = 0; + b->limit = 0; testBlocks = b; - return TestCBSBlock(b); + return b; } -static void DestroyCBSBlock(CBSBlock c) +static void DestroyTestBlock(TestBlock b) { - Test b = CBSBlockTest(c); - if (b == testBlocks) testBlocks = b->next; else { - Test prev; + TestBlock prev; for (prev = testBlocks; prev != 0; prev = prev->next) if (prev->next == b) { @@ -104,12 +92,13 @@ static void DestroyCBSBlock(CBSBlock c) static void step(void) { Res res; - CBSBlock a; + TestBlock a; switch (abqRnd(9)) { case 0: case 1: case 2: case 3: push: - res = ABQPush(&abq, CreateCBSBlock(pushee)); + a = CreateTestBlock(pushee); + res = ABQPush(&abq, (Addr)&a); if (res != ResOK) { goto pop; } @@ -117,7 +106,7 @@ static void step(void) break; case 5: case 6: case 7: case 8: pop: - res = ABQPop(&abq, &a); + res = ABQPop(&abq, (Addr)&a); if (res != ResOK){ goto push; } @@ -125,20 +114,20 @@ static void step(void) popee++; deleted = 0; } - cdie(CBSBlockTest(a)->id == popee, "pop"); + cdie(a->id == popee, "pop"); popee++; - DestroyCBSBlock(a); + DestroyTestBlock(a); break; default: if (!deleted & (pushee > popee)) { - Test b; + TestBlock b; deleted = (unsigned)abqRnd (pushee - popee) + popee; for (b = testBlocks; b != NULL; b = b->next) if (b->id == deleted) break; cdie(b != NULL, "found to delete"); - res = ABQDelete(&abq, TestCBSBlock(b)); + res = ABQDelete(&abq, (Addr)&b); cdie(res == ResOK, "ABQDelete"); } } @@ -159,7 +148,7 @@ extern int main(int argc, char *argv[]) die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "mps_arena_create"); - die(ABQInit((Arena)arena, &abq, NULL, ABQ_SIZE), + die(ABQInit((Arena)arena, &abq, NULL, ABQ_SIZE, sizeof(TestBlock)), "ABQInit"); abqSize = ABQ_SIZE; diff --git a/mps/design/abq.txt b/mps/design/abq.txt new file mode 100644 index 00000000000..92321512cc6 --- /dev/null +++ b/mps/design/abq.txt @@ -0,0 +1,178 @@ +.. mode:: -*- rst -*- + +Queue +===== + +:Tag: design.mps.abq +:Author: Gareth Rees +:Date: 2013-05-20 +:Status: complete design +:Revision: $Id$ +:Copyright: See section `C. Copyright and License`_. + + +Introduction +------------ + +_`.intro`: This is the design of the ABQ module, which implements a +fixed-length queue of small objects. + +_`readership`: This document is intended for any MM developer. + +_`name`: The name ABQ originally stood for "Available Block Queue" as +the module is used by the MVT pool. + + +Requirements +------------ + +_`.req.push` Clients can efficiently push new elements onto the queue. + +_`.req.pop` Clients can efficiently pop elements from the queue. + +_`.req.empty` Clients can efficiently test whether the queue is empty. + +_`.req.abstract` The ABQ module does not know anything about the +elements in the queue other than their size. + +_`.req.delete` Clients can delete elements from the queue. (Note: not necessarily efficiently.) + +_`.req.iterate` Clients can iterate over elements in the queue. + + +Interface +--------- + +.. c:type:: ABQ + +``ABQ`` is the type of a queue. It is an alias for ``ABQStruct *``. +``ABQStruct`` is defined in the header so that it can be inlined in +client structures: clients must not depend on its implementation +details. + +.. c:function:: ABQInit(Arena arena, ABQ abq, void *owner, Count elements, Size elementSize) + +Initialize the queue ``abq``. The parameter ``arena`` is the arena +whose control pool should be used to allocate the memory for the +queue; ``owner`` is passed to :c:func:`MeterInit` for the statistics; ``elements`` is the maximum number of elements that can be stored in the queue; and ``elementSize`` is the size of each element. + +.. c:function:: void ABQFinish(Arena arena, ABQ abq) + +Finish ``abq`` and free all resources associated with it. + +.. c:function:: Res ABQPush(ABQ abq, Addr element) + +If the queue is not full, push ``element`` on to the queue and return +``ResOK``. If the queue is full, leave it unchanged and return +``ResFAIL``. + +.. c:function:: Res ABQPop(ABQ abq, Addr elementReturn) + +If the queue is not empty, copy the first element on the queue into +the memory pointed to by ``elementReturn``, remove the element from +the queue, and return ``ResOK``. If the queue is empty, return +``ResFAIL``. + +.. c:function:: Res ABQPeek(ABQ abq, Addr elementReturn) + +If the queue is not empty, copy the first element on the queue into +the memory pointed to by ``elementReturn`` and return ``ResOK``. If +the queue is empty, return ``ResFAIL``. (This is the same as +:c:func:`ABQPop` except that the queue is unchanged.) + +.. c:function:: Res ABQDelete(ABQ abq, Addr element) + +If any elements in the queue compare equal to the element pointed to +by ``element``, delete them from the queue and return ``ResOK``. +Otherwise, return ``ResFAIL``. + +.. c:function:: Bool ABQIsEmpty(ABQ abq) + +If the queue is empty, return ``TRUE``, otherwise return ``FALSE``. + +.. c:function:: Bool ABQIsFull(ABQ abq) + +If the queue is full, return ``TRUE``, otherwise return ``FALSE``. + +.. c:function:: Count ABQDepth(ABQ abq) + +Return the number of elements in the queue. + +.. c:type:: Res (*ABQDescribeElement)(Addr element, mps_lib_FILE *stream) + +A function that can describe an element that has been pushed onto a queue. + +.. c:type:: typedef unsigned ABQDisposition + +An enumerated value returned by a function of +:c:type:`ABQIterateMethod` describing what should happen to an element +of a queue during the iteration. The value ``ABQDispositionKEEP`` +means that the element must be kept in the queue; +``ABQDispositionDELETE`` means that the element must be deleted from +the queue. + +.. c:type:: Res (*ABQIterateMethod)(ABQDisposition *dispositionReturn, Addr element, void *closureP) + +A callback function for :c:func:`ABQIterate`. The parameter +``element`` is an element in the queue, and ``closureP`` is the value +that was originally passed to ``ABQIterate``. This function must set +``*dispositionReturn`` to ``ABQDispositionKEEP`` if ``element`` must +be kept in the queue, or ``ABQDispositionDELETE`` if ``element`` must +be deleted from the queue. If it returns ``ResOK`` then iteration will +continue. If it returns any other result code, iteration will stop. + +.. c:function:: void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP) + +Call ``iterate`` for each elements in the queue, passing the element +and ``closureP``. See :c:type:`ABQIterateMethod` for details. + + +B. Document history +------------------- + +- 2013-05-20 GDR_ Created. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ + + +C. Copyright and License +------------------------ + +Copyright (C) 2013 Ravenbrook Limited. All rights reserved. +. This is an open source license. Contact +Ravenbrook for commercial licensing options. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Redistributions in any form must be accompanied by information on how + to obtain complete source code for this software and any + accompanying software that uses this software. The source code must + either be included in the distribution or be available for no more than + the cost of distribution plus a nominal fee, and must be freely + redistributable under reasonable conditions. For an executable file, + complete source code means the source code for all modules it contains. + It does not include source code for modules or files that typically + accompany the major components of the operating system on which the + executable file runs. + +**This software is provided by the copyright holders and contributors +"as is" and any express or implied warranties, including, but not +limited to, the implied warranties of merchantability, fitness for a +particular purpose, or non-infringement, are disclaimed. In no event +shall the copyright holders and contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or +services; loss of use, data, or profits; or business interruption) +however caused and on any theory of liability, whether in contract, +strict liability, or tort (including negligence or otherwise) arising in +any way out of the use of this software, even if advised of the +possibility of such damage.**