/* sac.c: SEGREGATED ALLOCATION CACHES
*
* $Id: //info.ravenbrook.com/project/mps/custom/cet/main/code/sac.c#5 $
* Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*/
#include "mpm.h"
#include "sac.h"
SRCID(sac, "$Id: //info.ravenbrook.com/project/mps/custom/cet/main/code/sac.c#5 $");
typedef _mps_sac_freelist_block_s *SACFreeListBlock;
/* SACCheck -- check function for SACs */
static Bool sacFreeListBlockCheck(SACFreeListBlock fb)
{
Count j;
Addr cb;
/* nothing to check about size */
CHECKL(fb->_count <= fb->_count_max);
/* check the freelist has the right number of blocks */
for (j = 0, cb = fb->_blocks; j < fb->_count; ++j) {
CHECKL(cb != NULL);
/* @@@@ ignoring shields for now */
cb = *ADDR_PTR(Addr, cb);
}
CHECKL(cb == NULL);
return TRUE;
}
ATTRIBUTE_UNUSED
static Bool SACCheck(SAC sac)
{
Index i, j;
Bool b;
Size prevSize;
mps_sac_t esac;
CHECKS(SAC, sac);
esac = ExternalSACOfSAC(sac);
CHECKU(Pool, sac->pool);
CHECKL(sac->classesCount > 0);
CHECKL(sac->classesCount > sac->middleIndex);
CHECKL(BoolCheck(esac->_trapped));
CHECKL(esac->_middle > 0);
/* check classes above middle */
prevSize = esac->_middle;
for (j = sac->middleIndex + 1, i = 0; j < sac->classesCount; ++j, i += 2) {
CHECKL(prevSize < esac->_freelists[i]._size);
b = sacFreeListBlockCheck(&(esac->_freelists[i]));
if (!b)
return b;
prevSize = esac->_freelists[i]._size;
}
/* check overlarge class */
CHECKL(prevSize < esac->_freelists[i]._size);
b = sacFreeListBlockCheck(&(esac->_freelists[i]));
if (!b)
return b;
CHECKL(esac->_freelists[i]._size == SizeMAX);
CHECKL(esac->_freelists[i]._count == 0);
CHECKL(esac->_freelists[i]._count_max == 0);
CHECKL(esac->_freelists[i]._blocks == NULL);
/* check classes below middle */
prevSize = esac->_middle;
for (j = sac->middleIndex, i = 1; j > 0; --j, i += 2) {
CHECKL(prevSize > esac->_freelists[i]._size);
b = sacFreeListBlockCheck(&(esac->_freelists[i]));
if (!b)
return b;
prevSize = esac->_freelists[i]._size;
}
/* check smallest class */
CHECKL(prevSize > esac->_freelists[i]._size);
CHECKL(esac->_freelists[i]._size == 0);
b = sacFreeListBlockCheck(&(esac->_freelists[i]));
return b;
}
/* sacSize -- calculate size of a SAC structure */
static Size sacSize(Index middleIndex, Count classesCount)
{
Index indexMax; /* max index for the freelist */
SACStruct dummy;
if (middleIndex + 1 < classesCount - middleIndex)
indexMax = 2 * (classesCount - middleIndex - 1);
else
indexMax = 1 + 2 * middleIndex;
return PointerOffset(&dummy, &dummy.esac_s._freelists[indexMax+1]);
}
/* SACCreate -- create an SAC object */
Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount,
SACClasses classes)
{
void *p;
SAC sac;
Res res;
Index i, j;
Index middleIndex; /* index of the size in the middle */
Size prevSize;
unsigned totalFreq = 0;
mps_sac_t esac;
AVER(sacReturn != NULL);
AVERT(Pool, pool);
AVER(classesCount > 0);
/* In this cache type, there is no upper limit on classesCount. */
prevSize = sizeof(Addr) - 1; /* must large enough for freelist link */
/* @@@@ It would be better to dynamically adjust the smallest class */
/* to be large enough, but that gets complicated, if you have to */
/* merge classes because of the adjustment. */
for (i = 0; i < classesCount; ++i) {
AVER(classes[i].mps_block_size > 0);
AVER(SizeIsAligned(classes[i].mps_block_size, PoolAlignment(pool)));
AVER(prevSize < classes[i].mps_block_size);
prevSize = classes[i].mps_block_size;
/* no restrictions on count */
/* no restrictions on frequency */
}
/* Calculate frequency scale */
for (i = 0; i < classesCount; ++i) {
unsigned oldFreq = totalFreq;
totalFreq += classes[i].mps_frequency;
AVER(oldFreq <= totalFreq); /* check for overflow */
UNUSED(oldFreq); /* <code/mpm.c#check.unused> */
}
/* Find middle one */
totalFreq /= 2;
for (i = 0; i < classesCount; ++i) {
if (totalFreq < classes[i].mps_frequency)
break;
totalFreq -= classes[i].mps_frequency;
}
if (totalFreq <= classes[i].mps_frequency / 2)
middleIndex = i;
else
middleIndex = i + 1; /* there must exist another class at i+1 */
/* Allocate SAC */
res = ControlAlloc(&p, PoolArena(pool), sacSize(middleIndex, classesCount));
if(res != ResOK)
goto failSACAlloc;
sac = p;
/* Move classes in place */
/* It's important this matches SACFind. */
esac = ExternalSACOfSAC(sac);
for (j = middleIndex + 1, i = 0; j < classesCount; ++j, i += 2) {
esac->_freelists[i]._size = classes[j].mps_block_size;
esac->_freelists[i]._count = 0;
esac->_freelists[i]._count_max = classes[j].mps_cached_count;
esac->_freelists[i]._blocks = NULL;
}
esac->_freelists[i]._size = SizeMAX;
esac->_freelists[i]._count = 0;
esac->_freelists[i]._count_max = 0;
esac->_freelists[i]._blocks = NULL;
for (j = middleIndex, i = 1; j > 0; --j, i += 2) {
esac->_freelists[i]._size = classes[j-1].mps_block_size;
esac->_freelists[i]._count = 0;
esac->_freelists[i]._count_max = classes[j].mps_cached_count;
esac->_freelists[i]._blocks = NULL;
}
esac->_freelists[i]._size = 0;
esac->_freelists[i]._count = 0;
esac->_freelists[i]._count_max = classes[j].mps_cached_count;
esac->_freelists[i]._blocks = NULL;
/* finish init */
esac->_trapped = FALSE;
esac->_middle = classes[middleIndex].mps_block_size;
sac->pool = pool;
sac->classesCount = classesCount;
sac->middleIndex = middleIndex;
sac->sig = SACSig;
AVERT(SAC, sac);
*sacReturn = sac;
return ResOK;
failSACAlloc:
return res;
}
/* SACDestroy -- destroy an SAC object */
void SACDestroy(SAC sac)
{
AVERT(SAC, sac);
SACFlush(sac);
sac->sig = SigInvalid;
ControlFree(PoolArena(sac->pool), sac,
sacSize(sac->middleIndex, sac->classesCount));
}
/* sacFind -- find the index corresponding to size
*
* This function replicates the loop in MPS_SAC_ALLOC_FAST, only with
* added checks.
*/
static void sacFind(Index *iReturn, Size *blockSizeReturn,
SAC sac, Size size)
{
Index i, j;
mps_sac_t esac;
esac = ExternalSACOfSAC(sac);
if (size > esac->_middle) {
i = 0; j = sac->middleIndex + 1;
AVER(j <= sac->classesCount);
while (size > esac->_freelists[i]._size) {
AVER(j < sac->classesCount);
i += 2; ++j;
}
*blockSizeReturn = esac->_freelists[i]._size;
} else {
Size prevSize = esac->_middle;
i = 1; j = sac->middleIndex;
while (size <= esac->_freelists[i]._size) {
AVER(j > 0);
prevSize = esac->_freelists[i]._size;
i += 2; --j;
}
*blockSizeReturn = prevSize;
}
*iReturn = i;
}
/* SACFill -- alloc an object, and perhaps fill the cache */
Res SACFill(Addr *p_o, SAC sac, Size size)
{
Index i;
Count blockCount, j;
Size blockSize;
Addr p, fl;
Res res = ResOK; /* stop compiler complaining */
mps_sac_t esac;
AVER(p_o != NULL);
AVERT(SAC, sac);
AVER(size != 0);
esac = ExternalSACOfSAC(sac);
sacFind(&i, &blockSize, sac, size);
/* Check it's empty (in the future, there will be other cases). */
AVER(esac->_freelists[i]._count == 0);
/* Fill 1/3 of the cache for this class. */
blockCount = esac->_freelists[i]._count_max / 3;
/* Adjust size for the overlarge class. */
if (blockSize == SizeMAX)
/* .align: align 'cause some classes don't accept unaligned. */
blockSize = SizeAlignUp(size, PoolAlignment(sac->pool));
for (j = 0, fl = esac->_freelists[i]._blocks;
j <= blockCount; ++j) {
res = PoolAlloc(&p, sac->pool, blockSize);
if (res != ResOK)
break;
/* @@@@ ignoring shields for now */
*ADDR_PTR(Addr, p) = fl; fl = p;
}
/* If didn't get any, just return. */
if (j == 0) {
AVER(res != ResOK);
return res;
}
/* Take the last one off, and return it. */
esac->_freelists[i]._count = j - 1;
*p_o = fl;
/* @@@@ ignoring shields for now */
esac->_freelists[i]._blocks = *ADDR_PTR(Addr, fl);
return ResOK;
}
/* sacClassFlush -- discard elements from the cache for a given class
*
* blockCount says how many elements to discard.
*/
static void sacClassFlush(SAC sac, Index i, Size blockSize,
Count blockCount)
{
Addr cb, fl;
Count j;
mps_sac_t esac;
esac = ExternalSACOfSAC(sac);
for (j = 0, fl = esac->_freelists[i]._blocks;
j < blockCount; ++j) {
/* @@@@ ignoring shields for now */
cb = fl; fl = *ADDR_PTR(Addr, cb);
PoolFree(sac->pool, cb, blockSize);
}
esac->_freelists[i]._count -= blockCount;
esac->_freelists[i]._blocks = fl;
}
/* SACEmpty -- free an object, and perhaps empty the cache */
void SACEmpty(SAC sac, Addr p, Size size)
{
Index i;
Size blockSize;
mps_sac_t esac;
AVERT(SAC, sac);
AVER(p != NULL);
AVER(PoolHasAddr(sac->pool, p));
AVER(size > 0);
esac = ExternalSACOfSAC(sac);
sacFind(&i, &blockSize, sac, size);
/* Check it's full (in the future, there will be other cases). */
AVER(esac->_freelists[i]._count
== esac->_freelists[i]._count_max);
/* Adjust size for the overlarge class. */
if (blockSize == SizeMAX)
/* see .align */
blockSize = SizeAlignUp(size, PoolAlignment(sac->pool));
if (esac->_freelists[i]._count_max > 0) {
Count blockCount;
/* Flush 2/3 of the cache for this class. */
/* Computed as count - count/3, so that the rounding works out right. */
blockCount = esac->_freelists[i]._count;
blockCount -= esac->_freelists[i]._count / 3;
sacClassFlush(sac, i, blockSize, (blockCount > 0) ? blockCount : 1);
/* Leave the current one in the cache. */
esac->_freelists[i]._count += 1;
/* @@@@ ignoring shields for now */
*ADDR_PTR(Addr, p) = esac->_freelists[i]._blocks;
esac->_freelists[i]._blocks = p;
} else {
/* Free even the current one. */
PoolFree(sac->pool, p, blockSize);
}
}
/* SACFlush -- flush the cache, releasing all memory held in it */
void SACFlush(SAC sac)
{
Index i, j;
Size prevSize;
mps_sac_t esac;
AVERT(SAC, sac);
esac = ExternalSACOfSAC(sac);
for (j = sac->middleIndex + 1, i = 0;
j < sac->classesCount; ++j, i += 2) {
sacClassFlush(sac, i, esac->_freelists[i]._size,
esac->_freelists[i]._count);
AVER(esac->_freelists[i]._blocks == NULL);
}
/* no need to flush overlarge, there's nothing there */
prevSize = esac->_middle;
for (j = sac->middleIndex, i = 1; j > 0; --j, i += 2) {
sacClassFlush(sac, i, prevSize, esac->_freelists[i]._count);
AVER(esac->_freelists[i]._blocks == NULL);
prevSize = esac->_freelists[i]._size;
}
/* flush smallest class */
sacClassFlush(sac, i, prevSize, esac->_freelists[i]._count);
AVER(esac->_freelists[i]._blocks == NULL);
}
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2014 Ravenbrook Limited <http://www.ravenbrook.com/>.
* 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.
*/