From 0a9858b956448866bbad108d0aa37605b04b0d30 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Mon, 29 Sep 2014 22:37:02 +0100 Subject: [PATCH 01/12] Branching master to branch/2014-09-29/reserved. Copied from Perforce Change: 187086 ServerID: perforce.ravenbrook.com From 9436487a051bb5d56ae696c9f6ec4f0c53255cc0 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Mon, 29 Sep 2014 22:50:46 +0100 Subject: [PATCH 02/12] Instead of iterating over the chunks to compute the total reserved address space, maintain a running total in arenachunkinsert and (new function) arenachunkremoved. New arena class method chunkReserved handles the class-specific computation of the reserved size of a chunk. Copied from Perforce Change: 187089 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 53 ++++++++++++++++++++++++++++++--------------- mps/code/arenacl.c | 34 ++++++++++------------------- mps/code/arenavm.c | 42 +++++++++++++++-------------------- mps/code/mpm.h | 1 + mps/code/mpmst.h | 5 +++-- mps/code/mpmtypes.h | 2 +- mps/code/tract.c | 7 +++--- 7 files changed, 72 insertions(+), 72 deletions(-) diff --git a/mps/code/arena.c b/mps/code/arena.c index 30a65479f9b..e8efe5d9bdb 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -85,13 +85,13 @@ DEFINE_CLASS(AbstractArenaClass, class) class->varargs = ArgTrivVarargs; class->init = NULL; class->finish = NULL; - class->reserved = NULL; class->purgeSpare = ArenaNoPurgeSpare; class->extend = ArenaNoExtend; class->grow = ArenaNoGrow; class->free = NULL; class->chunkInit = NULL; class->chunkFinish = NULL; + class->chunkReserved = NULL; class->compact = ArenaTrivCompact; class->describe = ArenaTrivDescribe; class->pagesMarkAllocated = NULL; @@ -113,13 +113,13 @@ Bool ArenaClassCheck(ArenaClass class) CHECKL(FUNCHECK(class->varargs)); CHECKL(FUNCHECK(class->init)); CHECKL(FUNCHECK(class->finish)); - CHECKL(FUNCHECK(class->reserved)); CHECKL(FUNCHECK(class->purgeSpare)); CHECKL(FUNCHECK(class->extend)); CHECKL(FUNCHECK(class->grow)); CHECKL(FUNCHECK(class->free)); CHECKL(FUNCHECK(class->chunkInit)); CHECKL(FUNCHECK(class->chunkFinish)); + CHECKL(FUNCHECK(class->chunkReserved)); CHECKL(FUNCHECK(class->compact)); CHECKL(FUNCHECK(class->describe)); CHECKL(FUNCHECK(class->pagesMarkAllocated)); @@ -206,6 +206,7 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) arena->class = class; + arena->reserved = (Size)0; arena->committed = (Size)0; /* commitLimit may be overridden by init (but probably not */ /* as there's not much point) */ @@ -454,7 +455,6 @@ void ControlFinish(Arena arena) Res ArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth) { Res res; - Size reserved; if (!TESTT(Arena, arena)) return ResFAIL; @@ -476,20 +476,9 @@ Res ArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth) return res; } - /* Note: this Describe clause calls a function */ - reserved = ArenaReserved(arena); res = WriteF(stream, depth + 2, - "reserved $W <-- " - "total size of address-space reserved\n", - (WriteFW)reserved, - NULL); - if (res != ResOK) - return res; - - res = WriteF(stream, depth + 2, - "committed $W <-- " - "total bytes currently stored (in RAM or swap)\n", - (WriteFW)arena->committed, + "reserved $W\n", (WriteFW)arena->reserved, + "committed $W\n", (WriteFW)arena->committed, "commitLimit $W\n", (WriteFW)arena->commitLimit, "spareCommitted $W\n", (WriteFW)arena->spareCommitted, "spareCommitLimit $W\n", (WriteFW)arena->spareCommitLimit, @@ -669,11 +658,15 @@ Res ControlDescribe(Arena arena, mps_lib_FILE *stream, Count depth) } -/* ArenaChunkInsert -- insert chunk into arena's chunk tree and ring */ +/* ArenaChunkInsert -- insert chunk into arena's chunk tree and ring, + * update the total reserved address space, and set the primary chunk + * if not already set. + */ void ArenaChunkInsert(Arena arena, Chunk chunk) { Bool inserted; Tree tree, updatedTree = NULL; + Size size; AVERT(Arena, arena); AVERT(Chunk, chunk); @@ -687,6 +680,9 @@ void ArenaChunkInsert(Arena arena, Chunk chunk) { arena->chunkTree = updatedTree; RingAppend(&arena->chunkRing, &chunk->arenaRing); + size = (*arena->class->chunkReserved)(chunk); + arena->reserved += size; + /* As part of the bootstrap, the first created chunk becomes the primary chunk. This step allows ArenaFreeLandInsert to allocate pages. */ if (arena->primary == NULL) @@ -694,6 +690,27 @@ void ArenaChunkInsert(Arena arena, Chunk chunk) { } +/* ArenaChunkRemoved -- chunk was removed from the arena and is being + * finished, so update the total reserved address space, and unset the + * primary chunk if necessary. + */ + +void ArenaChunkRemoved(Arena arena, Chunk chunk) +{ + Size size; + + AVERT(Arena, arena); + AVERT(Chunk, chunk); + + if (arena->primary == chunk) + arena->primary = NULL; + + size = (*arena->class->chunkReserved)(chunk); + AVER(arena->reserved >= size); + arena->reserved -= size; +} + + /* arenaAllocPage -- allocate one page from the arena * * This is a primitive allocator used to allocate pages for the arena @@ -1231,7 +1248,7 @@ allDeposited: Size ArenaReserved(Arena arena) { AVERT(Arena, arena); - return (*arena->class->reserved)(arena); + return arena->reserved; } Size ArenaCommitted(Arena arena) diff --git a/mps/code/arenacl.c b/mps/code/arenacl.c index e535ad770cc..685844c3fa4 100644 --- a/mps/code/arenacl.c +++ b/mps/code/arenacl.c @@ -206,6 +206,17 @@ static void ClientChunkFinish(Chunk chunk) } +/* ClientChunkReserved -- return the amount of reserved address space + * in a client chunk. + */ + +static Size ClientChunkReserved(Chunk chunk) +{ + AVERT(Chunk, chunk); + return ChunkSize(chunk); +} + + /* ClientArenaVarargs -- parse obsolete varargs */ static void ClientArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) @@ -344,27 +355,6 @@ static Res ClientArenaExtend(Arena arena, Addr base, Size size) } -/* ClientArenaReserved -- return the amount of reserved address space */ - -static Size ClientArenaReserved(Arena arena) -{ - Size size; - Ring node, nextNode; - - AVERT(Arena, arena); - - size = 0; - /* .req.extend.slow */ - RING_FOR(node, &arena->chunkRing, nextNode) { - Chunk chunk = RING_ELT(Chunk, arenaRing, node); - AVERT(Chunk, chunk); - size += ChunkSize(chunk); - } - - return size; -} - - /* ClientArenaPagesMarkAllocated -- Mark the pages allocated */ static Res ClientArenaPagesMarkAllocated(Arena arena, Chunk chunk, @@ -447,12 +437,12 @@ DEFINE_ARENA_CLASS(ClientArenaClass, this) this->varargs = ClientArenaVarargs; this->init = ClientArenaInit; this->finish = ClientArenaFinish; - this->reserved = ClientArenaReserved; this->extend = ClientArenaExtend; this->pagesMarkAllocated = ClientArenaPagesMarkAllocated; this->free = ClientFree; this->chunkInit = ClientChunkInit; this->chunkFinish = ClientChunkFinish; + this->chunkReserved = ClientChunkReserved; AVERT(ArenaClass, this); } diff --git a/mps/code/arenavm.c b/mps/code/arenavm.c index 97d2efe3e95..456876a6312 100644 --- a/mps/code/arenavm.c +++ b/mps/code/arenavm.c @@ -456,6 +456,17 @@ static void VMChunkFinish(Chunk chunk) } +/* VMChunkReserved -- return the amount of reserved address space in a + * VM chunk. + */ + +static Size VMChunkReserved(Chunk chunk) +{ + AVERT(Chunk, chunk); + return VMReserved(VMChunkVM(Chunk2VMChunk(chunk))); +} + + /* VMArenaVarargs -- parse obsolete varargs */ static void VMArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) @@ -648,25 +659,6 @@ static void VMArenaFinish(Arena arena) } -/* VMArenaReserved -- return the amount of reserved address space - * - * Add up the reserved space from all the chunks. - */ - -static Size VMArenaReserved(Arena arena) -{ - Size reserved; - Ring node, next; - - reserved = 0; - RING_FOR(node, &arena->chunkRing, next) { - VMChunk vmChunk = Chunk2VMChunk(RING_ELT(Chunk, arenaRing, node)); - reserved += VMReserved(VMChunkVM(vmChunk)); - } - return reserved; -} - - /* vmArenaChunkSize -- choose chunk size for arena extension * * .vmchunk.overhead: This code still lacks a proper estimate of @@ -717,7 +709,7 @@ static Res VMArenaGrow(Arena arena, LocusPref pref, Size size) chunkSize = vmArenaChunkSize(vmArena, size); EVENT3(vmArenaExtendStart, size, chunkSize, - VMArenaReserved(VMArena2Arena(vmArena))); + ArenaReserved(VMArena2Arena(vmArena))); /* .chunk-create.fail: If we fail, try again with a smaller size */ { @@ -741,7 +733,7 @@ static Res VMArenaGrow(Arena arena, LocusPref pref, Size size) for(; chunkSize > chunkHalf; chunkSize -= sliceSize) { if(chunkSize < chunkMin) { EVENT2(vmArenaExtendFail, chunkMin, - VMArenaReserved(VMArena2Arena(vmArena))); + ArenaReserved(VMArena2Arena(vmArena))); return res; } res = VMChunkCreate(&newChunk, vmArena, chunkSize); @@ -752,7 +744,7 @@ static Res VMArenaGrow(Arena arena, LocusPref pref, Size size) } vmArenaGrow_Done: - EVENT2(vmArenaExtendDone, chunkSize, VMArenaReserved(VMArena2Arena(vmArena))); + EVENT2(vmArenaExtendDone, chunkSize, ArenaReserved(VMArena2Arena(vmArena))); vmArena->extended(VMArena2Arena(vmArena), newChunk->base, AddrOffset(newChunk->base, newChunk->limit)); @@ -1136,7 +1128,7 @@ static void VMCompact(Arena arena, Trace trace) AVERT(VMArena, vmArena); AVERT(Trace, trace); - vmem1 = VMArenaReserved(arena); + vmem1 = ArenaReserved(arena); /* Destroy chunks that are completely free, but not the primary * chunk. See @@ -1146,7 +1138,7 @@ static void VMCompact(Arena arena, Trace trace) { Size vmem0 = trace->preTraceArenaReserved; - Size vmem2 = VMArenaReserved(arena); + Size vmem2 = ArenaReserved(arena); /* VMCompact event: emit for all client-requested collections, */ /* plus any others where chunks were gained or lost during the */ @@ -1196,12 +1188,12 @@ DEFINE_ARENA_CLASS(VMArenaClass, this) this->varargs = VMArenaVarargs; this->init = VMArenaInit; this->finish = VMArenaFinish; - this->reserved = VMArenaReserved; this->purgeSpare = VMPurgeSpare; this->grow = VMArenaGrow; this->free = VMFree; this->chunkInit = VMChunkInit; this->chunkFinish = VMChunkFinish; + this->chunkReserved = VMChunkReserved; this->compact = VMCompact; this->describe = VMArenaDescribe; this->pagesMarkAllocated = VMPagesMarkAllocated; diff --git a/mps/code/mpm.h b/mps/code/mpm.h index f1ed6812886..4cf2e26781c 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -566,6 +566,7 @@ extern Res ArenaCollect(Globals globals, int why); extern Bool ArenaHasAddr(Arena arena, Addr addr); extern Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr); extern void ArenaChunkInsert(Arena arena, Chunk chunk); +extern void ArenaChunkRemoved(Arena arena, Chunk chunk); extern void ArenaSetEmergency(Arena arena, Bool emergency); extern Bool ArenaEmergency(Arena arean); diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index ac59492bea5..96e8599e424 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -530,13 +530,13 @@ typedef struct mps_arena_class_s { ArenaVarargsMethod varargs; ArenaInitMethod init; ArenaFinishMethod finish; - ArenaReservedMethod reserved; ArenaPurgeSpareMethod purgeSpare; ArenaExtendMethod extend; ArenaGrowMethod grow; ArenaFreeMethod free; ArenaChunkInitMethod chunkInit; ArenaChunkFinishMethod chunkFinish; + ArenaChunkReservedMethod chunkReserved; ArenaCompactMethod compact; ArenaDescribeMethod describe; ArenaPagesMarkAllocatedMethod pagesMarkAllocated; @@ -718,7 +718,8 @@ typedef struct mps_arena_s { ReservoirStruct reservoirStruct; /* */ - Size committed; /* amount of committed RAM */ + Size reserved; /* total reserved address space */ + Size committed; /* total committed memory */ Size commitLimit; /* client-configurable commit limit */ Size spareCommitted; /* Amount of memory in hysteresis fund */ diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index 4c0b6bd9c87..de98355342a 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -120,12 +120,12 @@ typedef void (*ArenaVarargsMethod)(ArgStruct args[], va_list varargs); typedef Res (*ArenaInitMethod)(Arena *arenaReturn, ArenaClass class, ArgList args); typedef void (*ArenaFinishMethod)(Arena arena); -typedef Size (*ArenaReservedMethod)(Arena arena); typedef Size (*ArenaPurgeSpareMethod)(Arena arena, Size size); typedef Res (*ArenaExtendMethod)(Arena arena, Addr base, Size size); typedef Res (*ArenaGrowMethod)(Arena arena, LocusPref pref, Size size); typedef void (*ArenaFreeMethod)(Addr base, Size size, Pool pool); typedef Res (*ArenaChunkInitMethod)(Chunk chunk, BootBlock boot); +typedef Size (*ArenaChunkReservedMethod)(Chunk chunk); typedef void (*ArenaChunkFinishMethod)(Chunk chunk); typedef void (*ArenaCompactMethod)(Arena arena, Trace trace); typedef Res (*ArenaDescribeMethod)(Arena arena, mps_lib_FILE *stream, Count depth); diff --git a/mps/code/tract.c b/mps/code/tract.c index 5d3bffe9721..39f6bead95e 100644 --- a/mps/code/tract.c +++ b/mps/code/tract.c @@ -262,17 +262,16 @@ void ChunkFinish(Chunk chunk) PageIndexBase(chunk, chunk->allocBase), chunk->limit); + ArenaChunkRemoved(arena, chunk); + chunk->sig = SigInvalid; TreeFinish(&chunk->chunkTree); RingRemove(&chunk->arenaRing); - if (chunk->arena->primary == chunk) - chunk->arena->primary = NULL; - /* Finish all other fields before class finish, because they might be */ /* unmapped there. */ - (chunk->arena->class->chunkFinish)(chunk); + (*arena->class->chunkFinish)(chunk); } From 22aa2a084118b989130c49924866a229b78fd443 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 30 Sep 2014 11:02:11 +0100 Subject: [PATCH 03/12] Account for the reserved memory in the arena's own vm. (this was previously not included in arenareserved.) Make the computation in pageDescUnmap clearer (and not relying on arithmetic overflow). Copied from Perforce Change: 187091 ServerID: perforce.ravenbrook.com --- mps/code/arenavm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mps/code/arenavm.c b/mps/code/arenavm.c index 456876a6312..fe1f0f91e9a 100644 --- a/mps/code/arenavm.c +++ b/mps/code/arenavm.c @@ -565,6 +565,7 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) res = ArenaInit(arena, class, grainSize, args); if (res != ResOK) goto failArenaInit; + arena->reserved = VMReserved(vm); arena->committed = VMMapped(vm); /* Copy VM descriptor into its place in the arena. */ @@ -645,6 +646,7 @@ static void VMArenaFinish(Arena arena) RingFinish(&vmArena->spareRing); /* Destroying the chunks should leave only the arena's own VM. */ + AVER(arena->reserved == VMReserved(VMArenaVM(vmArena))); AVER(arena->committed == VMMapped(VMArenaVM(vmArena))); vmArena->sig = SigInvalid; @@ -798,10 +800,13 @@ static Res pageDescMap(VMChunk vmChunk, Index basePI, Index limitPI) static void pageDescUnmap(VMChunk vmChunk, Index basePI, Index limitPI) { + Size size; Size before = VMMapped(VMChunkVM(vmChunk)); Arena arena = VMArena2Arena(VMChunkVMArena(vmChunk)); SparseArrayUnmap(&vmChunk->pages, basePI, limitPI); - arena->committed += VMMapped(VMChunkVM(vmChunk)) - before; + size = before - VMMapped(VMChunkVM(vmChunk)); + AVER(arena->committed >= size); + arena->committed -= size; } From 05408fa99236e0d0c8ac669ede374613fc4bbefa Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 30 Sep 2014 18:57:50 +0100 Subject: [PATCH 04/12] Remove orphaned comment (originally from grm in change 19855) about being unable to "check that limit>=size" (referring to the reservoir). it seems, at least from testing, that this check is good now, so add it to reservoircheck. Copied from Perforce Change: 187092 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 3 --- mps/code/reserv.c | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mps/code/arena.c b/mps/code/arena.c index e8efe5d9bdb..c42b7e56cf9 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -142,9 +142,6 @@ Bool ArenaCheck(Arena arena) CHECKD(Reservoir, &arena->reservoirStruct); } - /* Can't check that limit>=size because we may call ArenaCheck */ - /* while the size is being adjusted. */ - CHECKL(arena->committed <= arena->commitLimit); CHECKL(arena->spareCommitted <= arena->committed); diff --git a/mps/code/reserv.c b/mps/code/reserv.c index b0d431c3f9c..91a2fd52559 100644 --- a/mps/code/reserv.c +++ b/mps/code/reserv.c @@ -100,6 +100,7 @@ Bool ReservoirCheck(Reservoir reservoir) } CHECKL(SizeIsArenaGrains(reservoir->reservoirLimit, arena)); CHECKL(SizeIsArenaGrains(reservoir->reservoirSize, arena)); + CHECKL(reservoir->reservoirSize <= reservoir->reservoirLimit); return TRUE; } From f01b9b7bb58267560cd6a28011c2318b1cafafef Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 30 Sep 2014 20:04:35 +0100 Subject: [PATCH 05/12] Document the behaviour of committed (memory marked as in use) and spare_committed (always zero) in the client arena class. Account for (and check) committed memory in the client arena class. Can't check arena->committed <= arena->reserved in ArenaCheck, but can in ClientArenaCheck. Rename ClientFree to ClientArenaFree to match the naming convention elsewhere. Copied from Perforce Change: 187093 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 6 +++++ mps/code/arenacl.c | 41 +++++++++++++++++++++++++------ mps/manual/source/topic/arena.rst | 30 ++++++++++++++++------ 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/mps/code/arena.c b/mps/code/arena.c index c42b7e56cf9..3a41ebbb0df 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -142,6 +142,12 @@ Bool ArenaCheck(Arena arena) CHECKD(Reservoir, &arena->reservoirStruct); } + /* .reserved.check: Would like to check that arena->committed <= + * arena->reserved, but that isn't always true in the VM arena. + * Memory is committed early on when VMChunkCreate calls VMArenaMap + * (to provide a place for the chunk struct) but is not recorded as + * reserved until ChunkInit calls ArenaChunkInsert. + */ CHECKL(arena->committed <= arena->commitLimit); CHECKL(arena->spareCommitted <= arena->committed); diff --git a/mps/code/arenacl.c b/mps/code/arenacl.c index 685844c3fa4..554dc963c72 100644 --- a/mps/code/arenacl.c +++ b/mps/code/arenacl.c @@ -81,8 +81,15 @@ static Bool ClientChunkCheck(ClientChunk clChunk) ATTRIBUTE_UNUSED static Bool ClientArenaCheck(ClientArena clientArena) { + Arena arena; + CHECKS(ClientArena, clientArena); - CHECKD(Arena, ClientArena2Arena(clientArena)); + arena = ClientArena2Arena(clientArena); + CHECKD(Arena, arena); + /* See */ + CHECKL(arena->committed <= arena->reserved); + CHECKL(arena->spareCommitted == 0); + return TRUE; } @@ -128,8 +135,8 @@ static Res clientChunkCreate(Chunk *chunkReturn, ClientArena clientArena, if (res != ResOK) goto failChunkInit; - ClientArena2Arena(clientArena)->committed += - AddrOffset(base, PageIndexBase(chunk, chunk->allocBase)); + arena->committed += ChunkPagesToSize(chunk, chunk->allocBase); + BootBlockFinish(boot); clChunk->sig = ClientChunkSig; @@ -176,8 +183,10 @@ static Res ClientChunkInit(Chunk chunk, BootBlock boot) static Bool clientChunkDestroy(Tree tree, void *closureP, Size closureS) { + Arena arena; Chunk chunk; ClientChunk clChunk; + Size size; AVERT(Tree, tree); AVER(closureP == UNUSED_POINTER); @@ -187,8 +196,15 @@ static Bool clientChunkDestroy(Tree tree, void *closureP, Size closureS) chunk = ChunkOfTree(tree); AVERT(Chunk, chunk); + arena = ChunkArena(chunk); + AVERT(Arena, arena); clChunk = Chunk2ClientChunk(chunk); AVERT(ClientChunk, clChunk); + AVER(chunk->pages == clChunk->freePages); + + size = ChunkPagesToSize(chunk, chunk->allocBase); + AVER(arena->committed >= size); + arena->committed -= size; clChunk->sig = SigInvalid; ChunkFinish(chunk); @@ -331,6 +347,10 @@ static void ClientArenaFinish(Arena arena) clientArena->sig = SigInvalid; + /* Destroying the chunks should leave nothing behind. */ + AVER(arena->reserved == 0); + AVER(arena->committed == 0); + ArenaFinish(arena); /* */ } @@ -362,9 +382,12 @@ static Res ClientArenaPagesMarkAllocated(Arena arena, Chunk chunk, Pool pool) { Index i; + ClientChunk clChunk; AVERT(Arena, arena); AVERT(Chunk, chunk); + clChunk = Chunk2ClientChunk(chunk); + AVERT(ClientChunk, clChunk); AVER(chunk->allocBase <= baseIndex); AVER(pages > 0); AVER(baseIndex + pages <= chunk->pages); @@ -373,15 +396,17 @@ static Res ClientArenaPagesMarkAllocated(Arena arena, Chunk chunk, for (i = 0; i < pages; ++i) PageAlloc(chunk, baseIndex + i, pool); - Chunk2ClientChunk(chunk)->freePages -= pages; + arena->committed += ChunkPagesToSize(chunk, pages); + AVER(clChunk->freePages >= pages); + clChunk->freePages -= pages; return ResOK; } -/* ClientFree - free a region in the arena */ +/* ClientArenaFree - free a region in the arena */ -static void ClientFree(Addr base, Size size, Pool pool) +static void ClientArenaFree(Addr base, Size size, Pool pool) { Arena arena; Chunk chunk = NULL; /* suppress "may be used uninitialized" */ @@ -422,6 +447,8 @@ static void ClientFree(Addr base, Size size, Pool pool) AVER(BTIsSetRange(chunk->allocTable, baseIndex, limitIndex)); BTResRange(chunk->allocTable, baseIndex, limitIndex); + AVER(arena->committed >= size); + arena->committed -= size; clChunk->freePages += pages; } @@ -439,7 +466,7 @@ DEFINE_ARENA_CLASS(ClientArenaClass, this) this->finish = ClientArenaFinish; this->extend = ClientArenaExtend; this->pagesMarkAllocated = ClientArenaPagesMarkAllocated; - this->free = ClientFree; + this->free = ClientArenaFree; this->chunkInit = ClientChunkInit; this->chunkFinish = ClientChunkFinish; this->chunkReserved = ClientChunkReserved; diff --git a/mps/manual/source/topic/arena.rst b/mps/manual/source/topic/arena.rst index ce313166161..a534135b9c8 100644 --- a/mps/manual/source/topic/arena.rst +++ b/mps/manual/source/topic/arena.rst @@ -380,9 +380,18 @@ Arena properties ``arena`` is the arena. - Returns the total amount of memory that has been committed to RAM + Returns the total amount of memory that has been committed for use by the MPS, in :term:`bytes (1)`. + For a :term:`virtual memory arena`, this is the amount of memory + mapped to RAM by the operating system's virtual memory interface. + + For a :term:`client arena`, this is the amount of memory marked as + in use in the arena's page tables. This is not particularly + meaningful by itself, but it corresponds to the amount of mapped + memory that the MPS would use if switched to a virtual memory + arena. + The committed memory is generally larger than the sum of the sizes of the allocated :term:`blocks`. The reasons for this are: @@ -412,7 +421,7 @@ Arena properties (over-)estimate the size of the heap. If you want to know how much memory the MPS is using then you're - probably interested in the value :c:func:`mps_arena_committed()` − + probably interested in the value :c:func:`mps_arena_committed` − :c:func:`mps_arena_spare_committed`. The amount of committed memory can be limited with the function @@ -436,12 +445,12 @@ Arena properties .. note:: - For a client arena, the reserved address may be lower than the - sum of the :c:macro:`MPS_KEY_ARENA_SIZE` keyword argument - passed to :c:func:`mps_arena_create_k` and the ``size`` - arguments passed to :c:func:`mps_arena_extend`, because the - arena may be unable to use the whole of each chunk for reasons - of alignment. + For a :term:`client arena`, the reserved address may be lower + than the sum of the :c:macro:`MPS_KEY_ARENA_SIZE` keyword + argument passed to :c:func:`mps_arena_create_k` and the + ``size`` arguments passed to :c:func:`mps_arena_extend`, + because the arena may be unable to use the whole of each chunk + for reasons of alignment. .. c:function:: size_t mps_arena_spare_commit_limit(mps_arena_t arena) @@ -506,6 +515,11 @@ Arena properties functions for limiting the amount of :term:`committed ` memory. + .. note:: + + :term:`Client arenas` do not use spare committed memory, and + so this function always returns 0. + .. index:: single: arena; states From 9ca8dc11bf8436f392a2fe8cc2ab2233e35f9080 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 30 Sep 2014 20:21:21 +0100 Subject: [PATCH 06/12] Update release notes for job001887. Copied from Perforce Change: 187096 ServerID: perforce.ravenbrook.com --- mps/manual/source/release.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst index fc9cc438c55..070980df085 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -17,6 +17,16 @@ Interface changes but is deprecated. +Other changes +............. + +#. :c:func:`mps_arena_committed` now returns a meaningful value (the + amount of memory marked as in use in the page tables) for + :term:`client arenas`. See job001887_. + + .. _job001887: https://www.ravenbrook.com/project/mps/issue/job001887/ + + .. _release-notes-1.114: Release 1.114.0 From 943471d0157e562103135b10d4a916fe924fd07e Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Wed, 1 Oct 2014 21:40:50 +0100 Subject: [PATCH 07/12] Store reserved address space associated with chunk in a field in the chunkstruct, as suggested by rb in . Copied from Perforce Change: 187104 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 8 ++------ mps/code/arenacl.c | 15 ++------------- mps/code/arenavm.c | 15 ++------------- mps/code/mpmst.h | 1 - mps/code/mpmtypes.h | 1 - mps/code/tract.c | 4 +++- mps/code/tract.h | 6 +++++- 7 files changed, 14 insertions(+), 36 deletions(-) diff --git a/mps/code/arena.c b/mps/code/arena.c index 3a41ebbb0df..05279ab6bd6 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -91,7 +91,6 @@ DEFINE_CLASS(AbstractArenaClass, class) class->free = NULL; class->chunkInit = NULL; class->chunkFinish = NULL; - class->chunkReserved = NULL; class->compact = ArenaTrivCompact; class->describe = ArenaTrivDescribe; class->pagesMarkAllocated = NULL; @@ -119,7 +118,6 @@ Bool ArenaClassCheck(ArenaClass class) CHECKL(FUNCHECK(class->free)); CHECKL(FUNCHECK(class->chunkInit)); CHECKL(FUNCHECK(class->chunkFinish)); - CHECKL(FUNCHECK(class->chunkReserved)); CHECKL(FUNCHECK(class->compact)); CHECKL(FUNCHECK(class->describe)); CHECKL(FUNCHECK(class->pagesMarkAllocated)); @@ -669,7 +667,6 @@ Res ControlDescribe(Arena arena, mps_lib_FILE *stream, Count depth) void ArenaChunkInsert(Arena arena, Chunk chunk) { Bool inserted; Tree tree, updatedTree = NULL; - Size size; AVERT(Arena, arena); AVERT(Chunk, chunk); @@ -683,8 +680,7 @@ void ArenaChunkInsert(Arena arena, Chunk chunk) { arena->chunkTree = updatedTree; RingAppend(&arena->chunkRing, &chunk->arenaRing); - size = (*arena->class->chunkReserved)(chunk); - arena->reserved += size; + arena->reserved += ChunkReserved(chunk); /* As part of the bootstrap, the first created chunk becomes the primary chunk. This step allows ArenaFreeLandInsert to allocate pages. */ @@ -708,7 +704,7 @@ void ArenaChunkRemoved(Arena arena, Chunk chunk) if (arena->primary == chunk) arena->primary = NULL; - size = (*arena->class->chunkReserved)(chunk); + size = ChunkReserved(chunk); AVER(arena->reserved >= size); arena->reserved -= size; } diff --git a/mps/code/arenacl.c b/mps/code/arenacl.c index 554dc963c72..5212faf702b 100644 --- a/mps/code/arenacl.c +++ b/mps/code/arenacl.c @@ -131,7 +131,8 @@ static Res clientChunkCreate(Chunk *chunkReturn, ClientArena clientArena, clChunk = p; chunk = ClientChunk2Chunk(clChunk); res = ChunkInit(chunk, arena, alignedBase, - AddrAlignDown(limit, ArenaGrainSize(arena)), boot); + AddrAlignDown(limit, ArenaGrainSize(arena)), + AddrOffset(base, limit), boot); if (res != ResOK) goto failChunkInit; @@ -222,17 +223,6 @@ static void ClientChunkFinish(Chunk chunk) } -/* ClientChunkReserved -- return the amount of reserved address space - * in a client chunk. - */ - -static Size ClientChunkReserved(Chunk chunk) -{ - AVERT(Chunk, chunk); - return ChunkSize(chunk); -} - - /* ClientArenaVarargs -- parse obsolete varargs */ static void ClientArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) @@ -469,7 +459,6 @@ DEFINE_ARENA_CLASS(ClientArenaClass, this) this->free = ClientArenaFree; this->chunkInit = ClientChunkInit; this->chunkFinish = ClientChunkFinish; - this->chunkReserved = ClientChunkReserved; AVERT(ArenaClass, this); } diff --git a/mps/code/arenavm.c b/mps/code/arenavm.c index fe1f0f91e9a..b37e7125fdd 100644 --- a/mps/code/arenavm.c +++ b/mps/code/arenavm.c @@ -323,7 +323,8 @@ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size) /* Copy VM descriptor into its place in the chunk. */ VMCopy(VMChunkVM(vmChunk), vm); - res = ChunkInit(VMChunk2Chunk(vmChunk), arena, base, limit, boot); + res = ChunkInit(VMChunk2Chunk(vmChunk), arena, base, limit, + VMReserved(VMChunkVM(vmChunk)), boot); if (res != ResOK) goto failChunkInit; @@ -456,17 +457,6 @@ static void VMChunkFinish(Chunk chunk) } -/* VMChunkReserved -- return the amount of reserved address space in a - * VM chunk. - */ - -static Size VMChunkReserved(Chunk chunk) -{ - AVERT(Chunk, chunk); - return VMReserved(VMChunkVM(Chunk2VMChunk(chunk))); -} - - /* VMArenaVarargs -- parse obsolete varargs */ static void VMArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) @@ -1198,7 +1188,6 @@ DEFINE_ARENA_CLASS(VMArenaClass, this) this->free = VMFree; this->chunkInit = VMChunkInit; this->chunkFinish = VMChunkFinish; - this->chunkReserved = VMChunkReserved; this->compact = VMCompact; this->describe = VMArenaDescribe; this->pagesMarkAllocated = VMPagesMarkAllocated; diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 96e8599e424..fecd90afa54 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -536,7 +536,6 @@ typedef struct mps_arena_class_s { ArenaFreeMethod free; ArenaChunkInitMethod chunkInit; ArenaChunkFinishMethod chunkFinish; - ArenaChunkReservedMethod chunkReserved; ArenaCompactMethod compact; ArenaDescribeMethod describe; ArenaPagesMarkAllocatedMethod pagesMarkAllocated; diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index de98355342a..b0d1fe2003a 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -125,7 +125,6 @@ typedef Res (*ArenaExtendMethod)(Arena arena, Addr base, Size size); typedef Res (*ArenaGrowMethod)(Arena arena, LocusPref pref, Size size); typedef void (*ArenaFreeMethod)(Addr base, Size size, Pool pool); typedef Res (*ArenaChunkInitMethod)(Chunk chunk, BootBlock boot); -typedef Size (*ArenaChunkReservedMethod)(Chunk chunk); typedef void (*ArenaChunkFinishMethod)(Chunk chunk); typedef void (*ArenaCompactMethod)(Arena arena, Trace trace); typedef Res (*ArenaDescribeMethod)(Arena arena, mps_lib_FILE *stream, Count depth); diff --git a/mps/code/tract.c b/mps/code/tract.c index 39f6bead95e..9aa815f47ba 100644 --- a/mps/code/tract.c +++ b/mps/code/tract.c @@ -167,7 +167,8 @@ Bool ChunkCheck(Chunk chunk) /* ChunkInit -- initialize generic part of chunk */ -Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, BootBlock boot) +Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, Size reserved, + BootBlock boot) { Size size; Count pages; @@ -192,6 +193,7 @@ Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, BootBlock boot) chunk->pageShift = pageShift = SizeLog2(chunk->pageSize); chunk->base = base; chunk->limit = limit; + chunk->reserved = reserved; size = ChunkSize(chunk); chunk->pages = pages = size >> pageShift; diff --git a/mps/code/tract.h b/mps/code/tract.h index 07906ad5756..ce7c602a176 100644 --- a/mps/code/tract.h +++ b/mps/code/tract.h @@ -148,6 +148,9 @@ typedef struct ChunkStruct { BT allocTable; /* page allocation table */ Page pageTable; /* the page table */ Count pageTablePages; /* number of pages occupied by page table */ + Size reserved; /* reserved address space for chunk (including overhead + such as losses due to alignment): must not change + (or arena reserved calculation will break) */ } ChunkStruct; @@ -159,10 +162,11 @@ typedef struct ChunkStruct { #define ChunkSizeToPages(chunk, size) ((Count)((size) >> (chunk)->pageShift)) #define ChunkPage(chunk, pi) (&(chunk)->pageTable[pi]) #define ChunkOfTree(tree) PARENT(ChunkStruct, chunkTree, tree) +#define ChunkReserved(chunk) RVALUE((chunk)->reserved) extern Bool ChunkCheck(Chunk chunk); extern Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, - BootBlock boot); + Size reserved, BootBlock boot); extern void ChunkFinish(Chunk chunk); extern Compare ChunkCompare(Tree tree, TreeKey key); extern TreeKey ChunkKey(Tree tree); From e2e51376f9b71d5f76b56dc96726a711603d4530 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 6 Nov 2014 10:51:18 +0000 Subject: [PATCH 08/12] Assert that sparsearrayunmap made some (or no) progress. Copied from Perforce Change: 187464 ServerID: perforce.ravenbrook.com --- mps/code/arenavm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mps/code/arenavm.c b/mps/code/arenavm.c index b37e7125fdd..7c4f44aa127 100644 --- a/mps/code/arenavm.c +++ b/mps/code/arenavm.c @@ -790,11 +790,13 @@ static Res pageDescMap(VMChunk vmChunk, Index basePI, Index limitPI) static void pageDescUnmap(VMChunk vmChunk, Index basePI, Index limitPI) { - Size size; + Size size, after; Size before = VMMapped(VMChunkVM(vmChunk)); Arena arena = VMArena2Arena(VMChunkVMArena(vmChunk)); SparseArrayUnmap(&vmChunk->pages, basePI, limitPI); - size = before - VMMapped(VMChunkVM(vmChunk)); + after = VMMapped(VMChunkVM(vmChunk)); + AVER(after <= before); + size = before - after; AVER(arena->committed >= size); arena->committed -= size; } From 2dc79898eb48b97a238c1e148cb3099385939910 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 6 Nov 2014 10:56:45 +0000 Subject: [PATCH 09/12] Check that sparsearraymap makes some (or no) progress. Copied from Perforce Change: 187465 ServerID: perforce.ravenbrook.com --- mps/code/arenavm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mps/code/arenavm.c b/mps/code/arenavm.c index 7c4f44aa127..81bea846cb7 100644 --- a/mps/code/arenavm.c +++ b/mps/code/arenavm.c @@ -784,7 +784,9 @@ static Res pageDescMap(VMChunk vmChunk, Index basePI, Index limitPI) Size before = VMMapped(VMChunkVM(vmChunk)); Arena arena = VMArena2Arena(VMChunkVMArena(vmChunk)); Res res = SparseArrayMap(&vmChunk->pages, basePI, limitPI); - arena->committed += VMMapped(VMChunkVM(vmChunk)) - before; + Size after = VMMapped(VMChunkVM(vmChunk)); + AVER(before <= after); + arena->committed += after - before; return res; } From 22769d3828a71bf07aa92afc813e3b8809eab3a6 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Thu, 5 Feb 2015 10:49:20 +0000 Subject: [PATCH 10/12] Add omitted word. Copied from Perforce Change: 187673 ServerID: perforce.ravenbrook.com --- mps/manual/source/topic/arena.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mps/manual/source/topic/arena.rst b/mps/manual/source/topic/arena.rst index a534135b9c8..53f5d5d3fc0 100644 --- a/mps/manual/source/topic/arena.rst +++ b/mps/manual/source/topic/arena.rst @@ -445,10 +445,10 @@ Arena properties .. note:: - For a :term:`client arena`, the reserved address may be lower - than the sum of the :c:macro:`MPS_KEY_ARENA_SIZE` keyword - argument passed to :c:func:`mps_arena_create_k` and the - ``size`` arguments passed to :c:func:`mps_arena_extend`, + For a :term:`client arena`, the reserved address space may be + lower than the sum of the :c:macro:`MPS_KEY_ARENA_SIZE` + keyword argument passed to :c:func:`mps_arena_create_k` and + the ``size`` arguments passed to :c:func:`mps_arena_extend`, because the arena may be unable to use the whole of each chunk for reasons of alignment. From 413a36fbcff00296d798ba3a450648441e1199aa Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 19 Jun 2015 12:01:01 +0100 Subject: [PATCH 11/12] Fix comment (function is named vmarenamap, not vmarenamap). Copied from Perforce Change: 187965 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mps/code/arena.c b/mps/code/arena.c index 05279ab6bd6..6327b791b59 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -142,7 +142,7 @@ Bool ArenaCheck(Arena arena) /* .reserved.check: Would like to check that arena->committed <= * arena->reserved, but that isn't always true in the VM arena. - * Memory is committed early on when VMChunkCreate calls VMArenaMap + * Memory is committed early on when VMChunkCreate calls vmArenaMap * (to provide a place for the chunk struct) but is not recorded as * reserved until ChunkInit calls ArenaChunkInsert. */ From 9863533bfc2a2b0b94b7b661ddd70e4e513c7af4 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 7 Aug 2015 16:14:50 +0100 Subject: [PATCH 12/12] The primary chunk is always the last chunk to be removed, so assert that. review suggestion from rb; see Copied from Perforce Change: 188089 ServerID: perforce.ravenbrook.com --- mps/code/arena.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mps/code/arena.c b/mps/code/arena.c index 6327b791b59..95355935c57 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -701,12 +701,16 @@ void ArenaChunkRemoved(Arena arena, Chunk chunk) AVERT(Arena, arena); AVERT(Chunk, chunk); - if (arena->primary == chunk) - arena->primary = NULL; - size = ChunkReserved(chunk); AVER(arena->reserved >= size); arena->reserved -= size; + + if (chunk == arena->primary) { + /* The primary chunk must be the last chunk to be removed. */ + AVER(RingIsSingle(&arena->chunkRing)); + AVER(arena->reserved == 0); + arena->primary = NULL; + } }