1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-03-27 01:01:52 -07:00

Merge branch/2014-04-22/condemn into the master sources.

Copied from Perforce
 Change: 185812
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Gareth Rees 2014-04-26 11:07:51 +01:00
commit 2c19172293
7 changed files with 185 additions and 161 deletions

View file

@ -27,7 +27,7 @@
#define totalSizeSTEP 200 * (size_t)1024
/* objNULL needs to be odd so that it's ignored in exactRoots. */
#define objNULL ((mps_addr_t)MPS_WORD_CONST(0xDECEA5ED))
#define testArenaSIZE ((size_t)16<<20)
#define testArenaSIZE ((size_t)1<<20)
#define initTestFREQ 3000
#define splatTestFREQ 6000
static mps_gen_param_s testChain[1] = { { 160, 0.90 } };
@ -107,7 +107,8 @@ static mps_addr_t make(void)
static mps_pool_debug_option_s freecheckOptions =
{ NULL, 0, (const void *)"Dead", 4 };
static void *test(void *arg, size_t haveAmbigous)
static void test_pool(mps_class_t pool_class, mps_arg_s args[],
size_t haveAmbiguous)
{
mps_pool_t pool;
mps_root_t exactRoot, ambigRoot = NULL;
@ -116,14 +117,13 @@ static void *test(void *arg, size_t haveAmbigous)
mps_ap_t busy_ap;
mps_addr_t busy_init;
pool = (mps_pool_t)arg;
die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create");
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
die(mps_ap_create(&busy_ap, pool, mps_rank_exact()), "BufferCreate 2");
for(i = 0; i < exactRootsCOUNT; ++i)
exactRoots[i] = objNULL;
if (haveAmbigous)
if (haveAmbiguous)
for(i = 0; i < ambigRootsCOUNT; ++i)
ambigRoots[i] = rnd_addr();
@ -132,7 +132,7 @@ static void *test(void *arg, size_t haveAmbigous)
&exactRoots[0], exactRootsCOUNT,
(mps_word_t)1),
"root_create_table(exact)");
if (haveAmbigous)
if (haveAmbiguous)
die(mps_root_create_table(&ambigRoot, arena,
mps_rank_ambig(), (mps_rm_t)0,
&ambigRoots[0], ambigRootsCOUNT),
@ -154,7 +154,7 @@ static void *test(void *arg, size_t haveAmbigous)
}
r = (size_t)rnd();
if (!haveAmbigous || (r & 1)) {
if (!haveAmbiguous || (r & 1)) {
i = (r >> 1) % exactRootsCOUNT;
if (exactRoots[i] != objNULL)
cdie(dylan_check(exactRoots[i]), "dying root check");
@ -187,80 +187,49 @@ static void *test(void *arg, size_t haveAmbigous)
mps_ap_destroy(busy_ap);
mps_ap_destroy(ap);
mps_root_destroy(exactRoot);
if (haveAmbigous)
if (haveAmbiguous)
mps_root_destroy(ambigRoot);
return NULL;
mps_pool_destroy(pool);
}
int main(int argc, char *argv[])
{
int i;
mps_thr_t thread;
mps_fmt_t format;
mps_chain_t chain;
mps_pool_t pool;
void *r;
testlib_init(argc, argv);
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
"arena_create");
die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "commit_limit_set");
mps_message_type_enable(arena, mps_message_type_gc_start());
mps_message_type_enable(arena, mps_message_type_gc());
die(mps_thread_reg(&thread, arena), "thread_reg");
die(mps_fmt_create_A(&format, arena, dylan_fmt_A()), "fmt_create");
die(mps_chain_create(&chain, arena, 1, testChain), "chain_create");
/* TODO: Add tests using the arena default chain. */
printf("\n\n****************************** Testing AMS Debug\n");
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format);
MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, FALSE);
MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions);
die(mps_pool_create_k(&pool, arena, mps_class_ams_debug(), args),
"pool_create(ams_debug,share)");
} MPS_ARGS_END(args);
mps_tramp(&r, test, pool, 0);
mps_pool_destroy(pool);
printf("\n\n****************************** Testing AMS Debug\n");
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format);
MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions);
die(mps_pool_create_k(&pool, arena, mps_class_ams_debug(), args),
"pool_create(ams_debug,ambig)");
} MPS_ARGS_END(args);
mps_tramp(&r, test, pool, 1);
mps_pool_destroy(pool);
printf("\n\n****************************** Testing AMS\n");
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format);
MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, TRUE);
MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions);
die(mps_pool_create_k(&pool, arena, mps_class_ams(), args),
"pool_create(ams,ambig)");
} MPS_ARGS_END(args);
mps_tramp(&r, test, pool, 1);
mps_pool_destroy(pool);
printf("\n\n****************************** Testing AMS\n");
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format);
MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, FALSE);
MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions);
die(mps_pool_create_k(&pool, arena, mps_class_ams(), args),
"pool_create(ams,share)");
} MPS_ARGS_END(args);
mps_tramp(&r, test, pool, 0);
mps_pool_destroy(pool);
for (i = 0; i < 8; i++) {
int debug = i % 2;
int ownChain = (i / 2) % 2;
int ambig = (i / 4) % 2;
printf("\n\n*** AMS%s with %sCHAIN and %sSUPPORT_AMBIGUOUS\n",
debug ? " Debug" : "",
ownChain ? "" : "!",
ambig ? "" : "!");
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format);
if (chain)
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, ambig);
MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions);
test_pool(debug ? mps_class_ams_debug() : mps_class_ams(), args, TRUE);
} MPS_ARGS_END(args);
}
mps_chain_destroy(chain);
mps_fmt_destroy(format);

View file

@ -275,9 +275,6 @@ static void *setup(void *v, size_t s)
die(mps_fmt_create_A(&dylanweakfmt, arena, dylan_fmt_A_weak()),
"Format Create (weak)\n");
MPS_ARGS_BEGIN(args) {
/* Ask the leafpool to allocate in the nursery, as we're using it to test
weaknesss and want things to die in it promptly. */
MPS_ARGS_ADD(args, MPS_KEY_GEN, 0);
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, dylanfmt);
die(mps_pool_create_k(&leafpool, arena, mps_class_lo(), args),
"Leaf Pool Create\n");

View file

@ -277,9 +277,6 @@ static void *setup(void *v, size_t s)
die(EnsureHeaderFormat(&dylanfmt, arena), "EnsureHeaderFormat");
die(EnsureHeaderWeakFormat(&dylanweakfmt, arena), "EnsureHeaderWeakFormat");
MPS_ARGS_BEGIN(args) {
/* Ask the leafpool to allocate in the nursery, as we're using it to test
weaknesss and want things to die in it promptly. */
MPS_ARGS_ADD(args, MPS_KEY_GEN, 0);
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, dylanfmt);
die(mps_pool_create_k(&leafpool, arena, mps_class_lo(), args),
"Leaf Pool Create\n");

View file

@ -81,7 +81,6 @@ extern Bool ChainCheck(Chain chain);
extern double ChainDeferral(Chain chain);
extern Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace);
extern Res ChainCondemnAll(Chain chain, Trace trace);
extern void ChainStartGC(Chain chain, Trace trace);
extern void ChainEndGC(Chain chain, Trace trace);
extern size_t ChainGens(Chain chain);

View file

@ -6,6 +6,20 @@
*
* DESIGN
*
* .mode: This test has two modes.
*
* .mode.park: In this mode, we use the arena's default generation
* chain, leave the arena parked and call mps_arena_collect. This
* tests that the default generation chain works and that all segments
* get condemned via TraceStartCollectAll. (See job003771 item 4.)
*
* .mode.poll: In this mode, we use our own generation chain (with
* small generations), allocate into generation 1, unclamp the arena,
* and provoke collection by allocating. This tests that custom
* generation chains work, and that segments get condemned via
* TracePoll even if there is no allocation into generation 0 of the
* chain. (See job003771 item 5.)
*
* DEPENDENCIES
*
* This test uses the dylan object format, but the reliance on this
@ -16,6 +30,7 @@
* This code was created by first copying <code/finalcv.c>
*/
#include "mpm.h"
#include "testlib.h"
#include "mpslib.h"
#include "mps.h"
@ -30,10 +45,15 @@
#include <stdio.h> /* fflush, printf, stdout */
enum {
ModePARK, /* .mode.park */
ModePOLL /* .mode.poll */
};
#define testArenaSIZE ((size_t)16<<20)
#define rootCOUNT 20
#define maxtreeDEPTH 10
#define maxtreeDEPTH 9
#define collectionCOUNT 10
@ -126,17 +146,21 @@ static mps_addr_t test_awl_find_dependent(mps_addr_t addr)
static void *root[rootCOUNT];
static void test_trees(const char *name, mps_arena_t arena, mps_ap_t ap,
static void test_trees(int mode, const char *name, mps_arena_t arena,
mps_ap_t ap,
mps_word_t (*make)(mps_word_t, mps_ap_t),
void (*reg)(mps_word_t, mps_arena_t))
{
size_t collections = 0;
size_t finals = 0;
size_t i;
int object_alloc;
object_count = 0;
printf("Making some %s finalized trees of objects.\n", name);
mps_arena_park(arena);
/* make some trees */
for(i = 0; i < rootCOUNT; ++i) {
root[i] = (void *)(*make)(maxtreeDEPTH, ap);
@ -151,10 +175,23 @@ static void test_trees(const char *name, mps_arena_t arena, mps_ap_t ap,
while (finals < object_count && collections < collectionCOUNT) {
mps_word_t final_this_time = 0;
printf("Collecting...");
(void)fflush(stdout);
die(mps_arena_collect(arena), "collect");
printf(" Done.\n");
switch (mode) {
default:
case ModePARK:
printf("Collecting...");
(void)fflush(stdout);
die(mps_arena_collect(arena), "collect");
printf(" Done.\n");
break;
case ModePOLL:
mps_arena_release(arena);
printf("Allocating...");
(void)fflush(stdout);
object_alloc = 0;
while (object_alloc < 1000 && !mps_message_poll(arena))
(void)DYLAN_INT(object_alloc++);
break;
}
++ collections;
while (mps_message_poll(arena)) {
mps_message_t message;
@ -170,10 +207,14 @@ static void test_trees(const char *name, mps_arena_t arena, mps_ap_t ap,
" of %"PRIuLONGEST"\n", (ulongest_t)final_this_time,
(ulongest_t)finals, (ulongest_t)object_count);
}
cdie(finals == object_count, "Not all objects were finalized.");
if (finals != object_count)
error("Not all objects were finalized for %s in mode %s.",
BufferOfAP(ap)->pool->class->name,
mode == ModePOLL ? "POLL" : "PARK");
}
static void *test(mps_arena_t arena, mps_class_t pool_class)
static void test_pool(int mode, mps_arena_t arena, mps_chain_t chain,
mps_class_t pool_class)
{
mps_ap_t ap;
mps_fmt_t fmt;
@ -182,10 +223,13 @@ static void *test(mps_arena_t arena, mps_class_t pool_class)
die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n");
MPS_ARGS_BEGIN(args) {
/* Allocate into generation 0 so that they get finalized quickly. */
MPS_ARGS_ADD(args, MPS_KEY_GEN, 0);
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt);
MPS_ARGS_ADD(args, MPS_KEY_AWL_FIND_DEPENDENT, test_awl_find_dependent);
if (mode == ModePOLL) {
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
MPS_ARGS_ADD(args, MPS_KEY_GEN, 1);
}
if (pool_class == mps_class_awl())
MPS_ARGS_ADD(args, MPS_KEY_AWL_FIND_DEPENDENT, test_awl_find_dependent);
die(mps_pool_create_k(&pool, arena, pool_class, args),
"pool_create\n");
} MPS_ARGS_END(args);
@ -194,19 +238,25 @@ static void *test(mps_arena_t arena, mps_class_t pool_class)
"root_create\n");
die(mps_ap_create(&ap, pool, mps_rank_exact()), "ap_create\n");
mps_message_type_enable(arena, mps_message_type_finalization());
mps_arena_park(arena);
test_trees("numbered", arena, ap, make_numbered_tree, register_numbered_tree);
test_trees("indirect", arena, ap, make_indirect_tree, register_indirect_tree);
test_trees(mode, "numbered", arena, ap, make_numbered_tree,
register_numbered_tree);
test_trees(mode, "indirect", arena, ap, make_indirect_tree,
register_indirect_tree);
mps_ap_destroy(ap);
mps_root_destroy(mps_root);
mps_pool_destroy(pool);
mps_fmt_destroy(fmt);
}
return NULL;
static void test_mode(int mode, mps_arena_t arena, mps_chain_t chain)
{
test_pool(mode, arena, chain, mps_class_amc());
test_pool(mode, arena, chain, mps_class_amcz());
test_pool(mode, arena, chain, mps_class_ams());
/* test_pool(mode, arena, chain, mps_class_lo()); TODO: job003773 */
/* test_pool(mode, arena, chain, mps_class_awl()); TODO: job003772 */
}
@ -214,19 +264,27 @@ int main(int argc, char *argv[])
{
mps_arena_t arena;
mps_thr_t thread;
mps_chain_t chain;
mps_gen_param_s params[2];
size_t gens = 2;
size_t i;
testlib_init(argc, argv);
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
"arena_create\n");
mps_message_type_enable(arena, mps_message_type_finalization());
die(mps_thread_reg(&thread, arena), "thread_reg\n");
for (i = 0; i < gens; ++i) {
params[i].mps_capacity = 1;
params[i].mps_mortality = 0.5;
}
die(mps_chain_create(&chain, arena, gens, params), "chain_create\n");
test(arena, mps_class_amc());
test(arena, mps_class_amcz());
test(arena, mps_class_ams());
test(arena, mps_class_awl());
/* TODO: test(arena, mps_class_lo()); */
test_mode(ModePOLL, arena, chain);
test_mode(ModePARK, arena, NULL);
mps_chain_destroy(chain);
mps_thread_dereg(thread);
mps_arena_destroy(arena);

View file

@ -287,13 +287,21 @@ Res ChainAlloc(Seg *segReturn, Chain chain, Serial genNr, SegClass class,
double ChainDeferral(Chain chain)
{
double time = DBL_MAX;
size_t i;
AVERT(Chain, chain);
if (chain->activeTraces != TraceSetEMPTY)
return DBL_MAX;
else
return chain->gens[0].capacity * 1024.0
- (double)GenDescNewSize(&chain->gens[0]);
if (chain->activeTraces == TraceSetEMPTY) {
for (i = 0; i < chain->genCount; ++i) {
double genTime = chain->gens[i].capacity * 1024.0
- (double)GenDescNewSize(&chain->gens[i]);
if (genTime < time)
time = genTime;
}
}
return time;
}
@ -306,7 +314,7 @@ double ChainDeferral(Chain chain)
Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace)
{
Res res;
Serial topCondemnedGenSerial, currGenSerial;
size_t topCondemnedGen, i;
GenDesc gen;
ZoneSet condemnedSet = ZoneSetEMPTY;
Size condemnedSize = 0, survivorSize = 0, genNewSize, genTotalSize;
@ -314,33 +322,39 @@ Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace)
AVERT(Chain, chain);
AVERT(Trace, trace);
/* Find lowest gen within its capacity, set topCondemnedGenSerial to the */
/* preceeding one. */
currGenSerial = 0;
gen = &chain->gens[0];
AVERT(GenDesc, gen);
genNewSize = GenDescNewSize(gen);
do { /* At this point, we've decided to collect currGenSerial. */
topCondemnedGenSerial = currGenSerial;
/* Find the highest generation that's over capacity. We will condemn
* this and all lower generations in the chain. */
topCondemnedGen = chain->genCount;
for (;;) {
/* It's an error to call this function unless some generation is
* over capacity as reported by ChainDeferral. */
AVER(topCondemnedGen > 0);
if (topCondemnedGen == 0)
return ResFAIL;
-- topCondemnedGen;
gen = &chain->gens[topCondemnedGen];
AVERT(GenDesc, gen);
genNewSize = GenDescNewSize(gen);
if (genNewSize >= gen->capacity * (Size)1024)
break;
}
/* At this point, we've decided to condemn topCondemnedGen and all
* lower generations. */
for (i = 0; i <= topCondemnedGen; ++i) {
gen = &chain->gens[i];
AVERT(GenDesc, gen);
condemnedSet = ZoneSetUnion(condemnedSet, gen->zones);
genTotalSize = GenDescTotalSize(gen);
genNewSize = GenDescNewSize(gen);
condemnedSize += genTotalSize;
survivorSize += (Size)(genNewSize * (1.0 - gen->mortality))
/* predict survivors will survive again */
+ (genTotalSize - genNewSize);
/* is there another one to consider? */
currGenSerial += 1;
if (currGenSerial >= chain->genCount)
break; /* reached the top */
gen = &chain->gens[currGenSerial];
AVERT(GenDesc, gen);
genNewSize = GenDescNewSize(gen);
} while (genNewSize >= gen->capacity * (Size)1024);
}
AVER(condemnedSet != ZoneSetEMPTY || condemnedSize == 0);
EVENT3(ChainCondemnAuto, chain, topCondemnedGenSerial, chain->genCount);
UNUSED(topCondemnedGenSerial); /* only used for EVENT */
EVENT3(ChainCondemnAuto, chain, topCondemnedGen, chain->genCount);
/* Condemn everything in these zones. */
if (condemnedSet != ZoneSetEMPTY) {
@ -354,41 +368,6 @@ Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace)
}
/* ChainCondemnAll -- condemn everything in the chain */
Res ChainCondemnAll(Chain chain, Trace trace)
{
Ring node, nextNode;
Bool haveWhiteSegs = FALSE;
Res res;
/* Condemn every segment in every pool using this chain. */
/* Finds the pools by iterating over the PoolGens in gen 0. */
RING_FOR(node, &chain->gens[0].locusRing, nextNode) {
PoolGen nursery = RING_ELT(PoolGen, genRing, node);
Pool pool = nursery->pool;
Ring segNode, nextSegNode;
AVERT(Pool, pool);
AVER(PoolHasAttr(pool, AttrGC));
RING_FOR(segNode, PoolSegRing(pool), nextSegNode) {
Seg seg = SegOfPoolRing(segNode);
res = TraceAddWhite(trace, seg);
if (res != ResOK)
goto failBegin;
haveWhiteSegs = TRUE;
}
}
return ResOK;
failBegin:
AVER(!haveWhiteSegs); /* Would leave white sets inconsistent. */
return res;
}
/* ChainStartGC -- called to notify start of GC for this chain */
void ChainStartGC(Chain chain, Trace trace)
@ -416,9 +395,11 @@ void ChainEndGC(Chain chain, Trace trace)
Res PoolGenInit(PoolGen gen, Chain chain, Serial nr, Pool pool)
{
/* Can't check gen, because it's not been initialized. */
AVER(gen != NULL);
AVERT(Chain, chain);
AVER(nr <= chain->genCount);
AVERT(Pool, pool);
AVER(PoolHasAttr(pool, AttrGC));
gen->nr = nr;
gen->pool = pool;

View file

@ -390,6 +390,7 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet)
Seg seg;
Arena arena;
Res res;
Bool haveWhiteSegs = FALSE;
AVERT(Trace, trace);
AVER(condemnedSet != ZoneSetEMPTY);
@ -415,7 +416,8 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet)
{
res = TraceAddWhite(trace, seg);
if(res != ResOK)
return res;
goto failBegin;
haveWhiteSegs = TRUE;
}
} while (SegNext(&seg, arena, seg));
}
@ -426,6 +428,10 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet)
AVER(ZoneSetSuper(condemnedSet, trace->white));
return ResOK;
failBegin:
AVER(!haveWhiteSegs); /* See .whiten.fail. */
return res;
}
@ -1503,21 +1509,31 @@ static Res traceCondemnAll(Trace trace)
{
Res res;
Arena arena;
Ring chainNode, nextChainNode;
Ring poolNode, nextPoolNode, chainNode, nextChainNode;
Bool haveWhiteSegs = FALSE;
arena = trace->arena;
AVERT(Arena, arena);
/* Condemn all the chains. */
RING_FOR(chainNode, &arena->chainRing, nextChainNode) {
Chain chain = RING_ELT(Chain, chainRing, chainNode);
AVERT(Chain, chain);
res = ChainCondemnAll(chain, trace);
if(res != ResOK)
goto failBegin;
haveWhiteSegs = TRUE;
/* Condemn all segments in pools with the GC attribute. */
RING_FOR(poolNode, &ArenaGlobals(arena)->poolRing, nextPoolNode) {
Pool pool = RING_ELT(Pool, arenaRing, poolNode);
AVERT(Pool, pool);
if (PoolHasAttr(pool, AttrGC)) {
Ring segNode, nextSegNode;
RING_FOR(segNode, PoolSegRing(pool), nextSegNode) {
Seg seg = SegOfPoolRing(segNode);
AVERT(Seg, seg);
res = TraceAddWhite(trace, seg);
if (res != ResOK)
goto failBegin;
haveWhiteSegs = TRUE;
}
}
}
/* Notify all the chains. */
RING_FOR(chainNode, &arena->chainRing, nextChainNode) {
Chain chain = RING_ELT(Chain, chainRing, chainNode);
@ -1527,7 +1543,14 @@ static Res traceCondemnAll(Trace trace)
return ResOK;
failBegin:
AVER(!haveWhiteSegs); /* Would leave white sets inconsistent. */
/* .whiten.fail: If we successfully whitened one or more segments,
* but failed to whiten them all, then the white sets would now be
* inconsistent. This can't happen in practice (at time of writing)
* because all PoolWhiten methods always succeed. If we ever have a
* pool class that fails to whiten a segment, then this assertion
* will be triggered. In that case, we'll have to recover here by
* blackening the segments again. */
AVER(!haveWhiteSegs);
return res;
}