diff --git a/.p4ignore b/.p4ignore
index ba720759d8..25cd419ae6 100644
--- a/.p4ignore
+++ b/.p4ignore
@@ -16,3 +16,8 @@ TAGS
*.dSYM
code/*/*/*.d
*.pyc
+test/obj
+test/test/log
+test/test/obj
+....gcda
+....gcno
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 07a0dd6652..d78b45e190 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,10 +2,19 @@
# $Id$
# See .
language: c
+os:
+ - linux
+ - osx
compiler:
- clang
- gcc
+matrix:
+ exclude:
+ - os: osx
+ compiler: gcc
notifications:
email:
- mps-travis@ravenbrook.com
irc: "irc.freenode.net#memorypoolsystem"
+script:
+ - ./configure --prefix=$PWD/prefix && make install && make test
diff --git a/Makefile.in b/Makefile.in
index 2d55858867..fe33a4d49f 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,7 +1,7 @@
# Makefile.in -- source for autoconf Makefile
#
# $Id$
-# Copyright (C) 2012-2013 Ravenbrook Limited. See end of file for license.
+# Copyright (C) 2012-2014 Ravenbrook Limited. See end of file for license.
#
# YOU DON'T NEED AUTOCONF TO BUILD THE MPS
# This is just here for people who want or expect a configure script.
@@ -13,11 +13,14 @@ INSTALL=@INSTALL@
INSTALL_DATA=@INSTALL_DATA@
INSTALL_PROGRAM=@INSTALL_PROGRAM@
MAKE=@MAKE@
-MPS_TARGET_NAME=@MPS_TARGET_NAME@
+MPS_OS_NAME=@MPS_OS_NAME@
+MPS_ARCH_NAME=@MPS_ARCH_NAME@
+MPS_BUILD_NAME=@MPS_BUILD_NAME@
+MPS_TARGET_NAME=$(MPS_OS_NAME)$(MPS_ARCH_NAME)$(MPS_BUILD_NAME)
EXTRA_TARGETS=@EXTRA_TARGETS@
prefix=$(DESTDIR)@prefix@
TARGET_OPTS=-C code -f $(MPS_TARGET_NAME).gmk EXTRA_TARGETS="$(EXTRA_TARGETS)"
-XCODEBUILD=xcodebuild -project code/mps.xcodeproj
+XCODEBUILD=xcrun xcodebuild -project code/mps.xcodeproj
all: @BUILD_TARGET@
@@ -31,15 +34,15 @@ install-make-build: make-install-dirs build-via-make
$(INSTALL_DATA) code/mps*.h $(prefix)/include/
$(INSTALL_DATA) code/$(MPS_TARGET_NAME)/cool/mps.a $(prefix)/lib/libmps-debug.a
$(INSTALL_DATA) code/$(MPS_TARGET_NAME)/hot/mps.a $(prefix)/lib/libmps.a
- $(INSTALL_PROGRAM) $(addprefix code/$(MPS_TARGET_NAME)/hot/Release/,$(EXTRA_TARGETS)) $(prefix)/bin
+ $(INSTALL_PROGRAM) $(addprefix code/$(MPS_TARGET_NAME)/hot/,$(EXTRA_TARGETS)) $(prefix)/bin
build-via-xcode:
- $(XCODEBUILD) -config Release
$(XCODEBUILD) -config Debug
+ $(XCODEBUILD) -config Release
clean-xcode-build:
- $(XCODEBUILD) -config Release clean
$(XCODEBUILD) -config Debug clean
+ $(XCODEBUILD) -config Release clean
install-xcode-build: make-install-dirs build-via-xcode
$(INSTALL_DATA) code/mps*.h $(prefix)/include/
@@ -67,12 +70,13 @@ make-install-dirs:
install: @INSTALL_TARGET@
-test-make-build: @BUILD_TARGET@
- $(MAKE) $(TARGET_OPTS) VARIETY=cool testrun
- $(MAKE) $(TARGET_OPTS) VARIETY=hot testrun
+test-make-build:
+ $(MAKE) $(TARGET_OPTS) testci
+ $(MAKE) -C code -f anan$(MPS_BUILD_NAME).gmk VARIETY=cool clean testansi
+ $(MAKE) -C code -f anan$(MPS_BUILD_NAME).gmk VARIETY=cool CFLAGS="-DCONFIG_POLL_NONE" clean testpollnone
test-xcode-build:
- $(XCODEBUILD) -config Release -target testrun
- $(XCODEBUILD) -config Debug -target testrun
+ $(XCODEBUILD) -config Debug -target testci
+ $(XCODEBUILD) -config Release -target testci
test: @TEST_TARGET@
diff --git a/NEWS b/NEWS
new file mode 120000
index 0000000000..6bf8922f3b
--- /dev/null
+++ b/NEWS
@@ -0,0 +1 @@
+manual/source/release.rst
\ No newline at end of file
diff --git a/code/.gdbinit b/code/.gdbinit
deleted file mode 100644
index cbdac5f7cc..0000000000
--- a/code/.gdbinit
+++ /dev/null
@@ -1 +0,0 @@
-handle SIGBUS nostop
diff --git a/code/.p4ignore b/code/.p4ignore
index 7a9dfb599a..6cb34b80f0 100644
--- a/code/.p4ignore
+++ b/code/.p4ignore
@@ -1,6 +1,9 @@
# code/.p4ignore -- Perforce files to ignore list
# $Id$
# Make output
+anangc
+ananll
+ananmv
fri3gc
fri6gc
lii3gc
@@ -9,6 +12,7 @@ lii6ll
w3i3mv
w3i6mv
xci3gc
+xci6ll
# Visual Studio junk
Debug
Release
diff --git a/code/abq.c b/code/abq.c
index 596921dd09..0e9b808a27 100644
--- a/code/abq.c
+++ b/code/abq.c
@@ -1,7 +1,7 @@
/* abq.c: QUEUE IMPLEMENTATION
*
* $Id$
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .purpose: A fixed-length FIFO queue.
*
@@ -107,7 +107,7 @@ Bool ABQPush(ABQ abq, void *element)
if (ABQIsFull(abq))
return FALSE;
- mps_lib_memcpy(ABQElement(abq, abq->in), element, abq->elementSize);
+ (void)mps_lib_memcpy(ABQElement(abq, abq->in), element, abq->elementSize);
abq->in = ABQNextIndex(abq, abq->in);
AVERT(ABQ, abq);
@@ -126,7 +126,7 @@ Bool ABQPop(ABQ abq, void *elementReturn)
if (ABQIsEmpty(abq))
return FALSE;
- mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize);
+ (void)mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize);
abq->out = ABQNextIndex(abq, abq->out);
@@ -146,7 +146,7 @@ Bool ABQPeek(ABQ abq, void *elementReturn)
if (ABQIsEmpty(abq))
return FALSE;
- mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize);
+ (void)mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize);
/* Identical to pop, but don't increment out */
@@ -156,49 +156,40 @@ Bool ABQPeek(ABQ abq, void *elementReturn)
/* ABQDescribe -- Describe an ABQ */
-Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream)
+Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream, Count depth)
{
Res res;
Index index;
- if (!TESTT(ABQ, abq)) return ResFAIL;
- if (stream == NULL) return ResFAIL;
-
- res = WriteF(stream,
- "ABQ $P\n{\n", (WriteFP)abq,
- " elements: $U \n", (WriteFU)abq->elements,
- " in: $U \n", (WriteFU)abq->in,
- " out: $U \n", (WriteFU)abq->out,
- " queue: \n",
+ if (!TESTT(ABQ, abq))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
+
+ res = WriteF(stream, depth,
+ "ABQ $P {\n", (WriteFP)abq,
+ " elements $U\n", (WriteFU)abq->elements,
+ " elementSize $W\n", (WriteFW)abq->elementSize,
+ " in $U\n", (WriteFU)abq->in,
+ " out $U\n", (WriteFU)abq->out,
+ " queue:\n",
NULL);
if(res != ResOK)
return res;
for (index = abq->out; index != abq->in; ) {
- res = (*describeElement)(ABQElement(abq, index), stream);
+ res = (*describeElement)(ABQElement(abq, index), stream, depth + 2);
if(res != ResOK)
return res;
index = ABQNextIndex(abq, index);
}
- res = WriteF(stream, "\n", NULL);
- if(res != ResOK)
- return res;
+ METER_WRITE(abq->push, stream, depth + 2);
+ METER_WRITE(abq->pop, stream, depth + 2);
+ METER_WRITE(abq->peek, stream, depth + 2);
+ METER_WRITE(abq->delete, stream, depth + 2);
- res = METER_WRITE(abq->push, stream);
- if(res != ResOK)
- return res;
- res = METER_WRITE(abq->pop, stream);
- if(res != ResOK)
- return res;
- res = METER_WRITE(abq->peek, stream);
- if(res != ResOK)
- return res;
- res = METER_WRITE(abq->delete, stream);
- if(res != ResOK)
- return res;
-
- res = WriteF(stream, "}\n", NULL);
+ res = WriteF(stream, depth, "} ABQ $P\n", (WriteFP)abq, NULL);
if(res != ResOK)
return res;
@@ -240,13 +231,13 @@ Count ABQDepth(ABQ abq)
}
-/* ABQIterate -- call 'iterate' for each element in an ABQ */
-void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP, Size closureS)
+/* ABQIterate -- call 'visitor' for each element in an ABQ */
+void ABQIterate(ABQ abq, ABQVisitor visitor, void *closureP, Size closureS)
{
Index copy, index, in;
AVERT(ABQ, abq);
- AVER(FUNCHECK(iterate));
+ AVER(FUNCHECK(visitor));
copy = abq->out;
index = abq->out;
@@ -256,12 +247,12 @@ void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP, Size closureS
void *element = ABQElement(abq, index);
Bool delete = FALSE;
Bool cont;
- cont = (*iterate)(&delete, element, closureP, closureS);
+ cont = (*visitor)(&delete, element, closureP, closureS);
AVERT(Bool, cont);
AVERT(Bool, delete);
if (!delete) {
if (copy != index)
- mps_lib_memcpy(ABQElement(abq, copy), element, abq->elementSize);
+ (void)mps_lib_memcpy(ABQElement(abq, copy), element, abq->elementSize);
copy = ABQNextIndex(abq, copy);
}
index = ABQNextIndex(abq, index);
@@ -272,8 +263,8 @@ void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP, Size closureS
/* 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);
+ (void)mps_lib_memcpy(ABQElement(abq, copy), ABQElement(abq, index),
+ abq->elementSize);
copy = ABQNextIndex(abq, copy);
index = ABQNextIndex(abq, index);
}
@@ -311,7 +302,7 @@ static void *ABQElement(ABQ abq, Index index) {
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/abq.h b/code/abq.h
index 022fc7e530..85cdfcd575 100644
--- a/code/abq.h
+++ b/code/abq.h
@@ -1,7 +1,7 @@
/* abq.h: QUEUE INTERFACE
*
* $Id$
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .purpose: A fixed-length FIFO queue.
*
@@ -23,8 +23,8 @@
/* Prototypes */
typedef struct ABQStruct *ABQ;
-typedef Res (*ABQDescribeElement)(void *element, mps_lib_FILE *stream);
-typedef Bool (*ABQIterateMethod)(Bool *deleteReturn, void *element, void *closureP, Size closureS);
+typedef Res (*ABQDescribeElement)(void *element, mps_lib_FILE *stream, Count depth);
+typedef Bool (*ABQVisitor)(Bool *deleteReturn, void *element, void *closureP, Size closureS);
extern Res ABQInit(Arena arena, ABQ abq, void *owner, Count elements, Size elementSize);
extern Bool ABQCheck(ABQ abq);
@@ -32,11 +32,11 @@ extern void ABQFinish(Arena arena, ABQ abq);
extern Bool ABQPush(ABQ abq, void *element);
extern Bool ABQPop(ABQ abq, void *elementReturn);
extern Bool ABQPeek(ABQ abq, void *elementReturn);
-extern Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream);
+extern Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream, Count depth);
extern Bool ABQIsEmpty(ABQ abq);
extern Bool ABQIsFull(ABQ abq);
extern Count ABQDepth(ABQ abq);
-extern void ABQIterate(ABQ abq, ABQIterateMethod iterate, void *closureP, Size closureS);
+extern void ABQIterate(ABQ abq, ABQVisitor visitor, void *closureP, Size closureS);
/* Types */
@@ -63,7 +63,7 @@ typedef struct ABQStruct
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/abqtest.c b/code/abqtest.c
index f8e3a2a4bd..9aad3351cb 100644
--- a/code/abqtest.c
+++ b/code/abqtest.c
@@ -1,21 +1,22 @@
/* abqtest.c: AVAILABLE BLOCK QUEUE TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*/
#include "abq.h"
#include "mps.h"
#include "mpsavm.h"
+#include "mpscmfs.h"
#include "mpstd.h"
#include "testlib.h"
-#include
+#include /* printf */
SRCID(abqtest, "$Id$");
-
+static mps_pool_t pool;
static ABQStruct abq; /* the ABQ which we will use */
static Size abqSize; /* the size of the current ABQ */
@@ -50,9 +51,12 @@ static TestBlock testBlocks = NULL;
static TestBlock CreateTestBlock(unsigned no)
{
- TestBlock b = malloc(sizeof(TestBlockStruct));
- cdie(b != NULL, "malloc");
+ TestBlock b;
+ mps_addr_t p;
+
+ die(mps_alloc(&p, pool, sizeof(TestBlockStruct)), "alloc");
+ b = p;
b->next = testBlocks;
b->id = no;
b->base = 0;
@@ -78,7 +82,7 @@ static void DestroyTestBlock(TestBlock b)
}
}
- free(b);
+ mps_free(pool, b, sizeof(TestBlockStruct));
}
typedef struct TestClosureStruct *TestClosure;
@@ -92,6 +96,7 @@ static Bool TestDeleteCallback(Bool *deleteReturn, void *element,
{
TestBlock *a = (TestBlock *)element;
TestClosure cl = (TestClosure)closureP;
+ AVER(closureS == UNUSED_SIZE);
UNUSED(closureS);
if (*a == cl->b) {
*deleteReturn = TRUE;
@@ -130,7 +135,7 @@ static void step(void)
DestroyTestBlock(a);
break;
default:
- if (!deleted & (pushee > popee)) {
+ if (!deleted && (pushee > popee)) {
TestBlock b;
TestClosureStruct cl;
deleted = (unsigned)abqRnd (pushee - popee) + popee;
@@ -140,28 +145,29 @@ static void step(void)
cdie(b != NULL, "found to delete");
cl.b = b;
cl.res = ResFAIL;
- ABQIterate(&abq, TestDeleteCallback, &cl, 0);
+ ABQIterate(&abq, TestDeleteCallback, &cl, UNUSED_SIZE);
cdie(cl.res == ResOK, "ABQIterate");
}
}
}
-
-#define testArenaSIZE (((size_t)4)<<20)
-
extern int main(int argc, char *argv[])
{
mps_arena_t arena;
int i;
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
+ testlib_init(argc, argv);
abqSize = 0;
- die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
+ die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none),
"mps_arena_create");
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, sizeof(TestBlockStruct));
+ die(mps_pool_create_k(&pool, arena, mps_class_mfs(), args), "pool_create");
+ } MPS_ARGS_END(args);
+
die(ABQInit((Arena)arena, &abq, NULL, ABQ_SIZE, sizeof(TestBlock)),
"ABQInit");
@@ -178,7 +184,7 @@ extern int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/airtest.c b/code/airtest.c
new file mode 100644
index 0000000000..c262cb3126
--- /dev/null
+++ b/code/airtest.c
@@ -0,0 +1,204 @@
+/* airtest.c: AMBIGUOUS INTERIOR REFERENCE TEST
+ *
+ * $Id: //info.ravenbrook.com/project/mps/branch/2014-01-15/nailboard/code/fotest.c#1 $
+ * Copyright (c) 2014 Ravenbrook Limited. See end of file for license.
+ *
+ * .overview: This test case creates a bunch of vectors, registers
+ * them for finalization, and then discards the base pointers to those
+ * objects, keeping only ambiguous interior references to the vector
+ * entries in the stack-allocated table s.
+ *
+ * .options: The test has two options:
+ *
+ * 'interior' is the value passed as MPS_KEY_INTERIOR when creating
+ * the AMC pool. If TRUE, interior pointers must keep objects alive,
+ * and so if any of these objects are finalized, the test fails. If
+ * FALSE, interior pointers do not keep objects alive, so it is likely
+ * that all the objects will be finalized.
+ *
+ * 'stack' is TRUE if the C stack is registered as a root. (If FALSE,
+ * we register the table of interior pointers as an ambiguous root.)
+ *
+ * .fail.lii6ll: The test case passes on most platforms with
+ * interior=FALSE and stack=TRUE (that is, all vectors get finalized),
+ * but fails on lii6ll in variety HOT. Rather than struggle to defeat
+ * the Clang optimizer, we choose not to test in this configuration.
+ * In any case, the MPS does not guarantee anything about timely
+ * finalization (see ).
+ */
+
+#include "mps.h"
+#include "mpsavm.h"
+#include "mpscamc.h"
+#include "mpslib.h"
+#include "testlib.h"
+#include "fmtscheme.h"
+
+#define OBJ_LEN (1u << 4)
+#define OBJ_COUNT 10
+
+static void test_air(int interior, int stack)
+{
+ size_t n_finalized = 0;
+ size_t i, j;
+ obj_t *s[OBJ_COUNT] = {0};
+ mps_root_t root = NULL;
+ if (!stack) {
+ mps_addr_t *p = (void *)s;
+ die(mps_root_create_table(&root, scheme_arena, mps_rank_ambig(), 0, p,
+ OBJ_COUNT), "mps_root_create_table");
+ }
+ mps_message_type_enable(scheme_arena, mps_message_type_finalization());
+ for (j = 0; j < OBJ_COUNT; ++j) {
+ obj_t n = scheme_make_integer(obj_ap, (long)j);
+ obj_t obj = scheme_make_vector(obj_ap, OBJ_LEN, n);
+ mps_addr_t ref = obj;
+ mps_finalize(scheme_arena, &ref);
+ s[j] = obj->vector.vector;
+ }
+ for (i = 1; i < OBJ_LEN; ++i) {
+ obj_t n = scheme_make_integer(obj_ap, (long)i);
+ mps_message_t msg;
+ for (j = 0; j + 1 < OBJ_COUNT; ++j) {
+ *++s[j] = n;
+ }
+ mps_arena_collect(scheme_arena);
+ mps_arena_release(scheme_arena);
+ if (mps_message_get(&msg, scheme_arena, mps_message_type_finalization())) {
+ mps_addr_t ref;
+ mps_message_finalization_ref(&ref, scheme_arena, msg);
+ ++ n_finalized;
+ if (interior) {
+ obj_t o;
+ o = ref;
+ error("wrongly finalized vector %ld at %p",
+ o->vector.vector[0]->integer.integer, (void *)o);
+ }
+ }
+ }
+ if (!interior && n_finalized < OBJ_COUNT) {
+ error("only finalized %"PRIuLONGEST" out of %"PRIuLONGEST" vectors.",
+ (ulongest_t)n_finalized, (ulongest_t)OBJ_COUNT);
+ }
+ if (!stack) {
+ mps_root_destroy(root);
+ }
+}
+
+static mps_gen_param_s obj_gen_params[] = {
+ { 150, 0.85 },
+ { 170, 0.45 }
+};
+
+static void test_main(void *marker, int interior, int stack)
+{
+ mps_res_t res;
+ mps_chain_t obj_chain;
+ mps_fmt_t obj_fmt;
+ mps_thr_t thread;
+ mps_root_t reg_root = NULL;
+
+ res = mps_arena_create_k(&scheme_arena, mps_arena_class_vm(), mps_args_none);
+ if (res != MPS_RES_OK)
+ error("Couldn't create arena");
+
+ res = mps_chain_create(&obj_chain, scheme_arena,
+ sizeof(obj_gen_params) / sizeof(*obj_gen_params),
+ obj_gen_params);
+ if (res != MPS_RES_OK)
+ error("Couldn't create obj chain");
+
+ scheme_fmt(&obj_fmt);
+
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_CHAIN, obj_chain);
+ MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt);
+ MPS_ARGS_ADD(args, MPS_KEY_INTERIOR, interior);
+ die(mps_pool_create_k(&obj_pool, scheme_arena, mps_class_amc(), args),
+ "mps_pool_create_k");
+ } MPS_ARGS_END(args);
+
+ res = mps_ap_create_k(&obj_ap, obj_pool, mps_args_none);
+ if (res != MPS_RES_OK)
+ error("Couldn't create obj allocation point");
+
+ res = mps_thread_reg(&thread, scheme_arena);
+ if (res != MPS_RES_OK)
+ error("Couldn't register thread");
+
+ if (stack) {
+ res = mps_root_create_reg(®_root, scheme_arena, mps_rank_ambig(), 0,
+ thread, mps_stack_scan_ambig, marker, 0);
+ if (res != MPS_RES_OK)
+ error("Couldn't create root");
+ }
+
+ test_air(interior, stack);
+
+ mps_arena_park(scheme_arena);
+ if (stack)
+ mps_root_destroy(reg_root);
+ mps_thread_dereg(thread);
+ mps_ap_destroy(obj_ap);
+ mps_pool_destroy(obj_pool);
+ mps_chain_destroy(obj_chain);
+ mps_fmt_destroy(obj_fmt);
+ mps_arena_destroy(scheme_arena);
+}
+
+int main(int argc, char *argv[])
+{
+ void *marker = ▮
+
+ testlib_init(argc, argv);
+
+ test_main(marker, TRUE, TRUE);
+ test_main(marker, TRUE, FALSE);
+ /* not test_main(marker, FALSE, TRUE) -- see .fail.lii6ll. */
+ test_main(marker, FALSE, FALSE);
+
+ printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
+ return 0;
+}
+
+
+/* C. COPYRIGHT AND LICENSE
+ *
+ * Copyright (c) 2014 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.
+ */
diff --git a/code/amcss.c b/code/amcss.c
index e3bb32627e..294ce224b1 100644
--- a/code/amcss.c
+++ b/code/amcss.c
@@ -1,30 +1,28 @@
/* amcss.c: POOL CLASS AMC STRESS TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (C) 2002 Global Graphics Software.
*/
#include "fmtdy.h"
#include "fmtdytst.h"
#include "testlib.h"
+#include "mpm.h"
#include "mpslib.h"
#include "mpscamc.h"
#include "mpsavm.h"
#include "mpstd.h"
-#ifdef MPS_OS_W3
-#include "mpsw3.h"
-#endif
#include "mps.h"
#include "mpslib.h"
-#include
-#include
+
+#include /* fflush, printf, putchar */
/* These values have been tuned in the hope of getting one dynamic collection. */
#define testArenaSIZE ((size_t)1000*1024)
-#define gen1SIZE ((size_t)150)
-#define gen2SIZE ((size_t)170)
+#define gen1SIZE ((size_t)20)
+#define gen2SIZE ((size_t)85)
#define avLEN 3
#define exactRootsCOUNT 180
#define ambigRootsCOUNT 50
@@ -46,14 +44,15 @@ static mps_gen_param_s testChain[genCOUNT] = {
static mps_ap_t ap;
static mps_addr_t exactRoots[exactRootsCOUNT];
static mps_addr_t ambigRoots[ambigRootsCOUNT];
+static size_t scale; /* Overall scale factor. */
+static unsigned long nCollsStart;
+static unsigned long nCollsDone;
/* report -- report statistics from any messages */
static void report(mps_arena_t arena)
{
- static int nCollsStart = 0;
- static int nCollsDone = 0;
mps_message_type_t type;
while(mps_message_queue_type(&type, arena)) {
@@ -63,7 +62,7 @@ static void report(mps_arena_t arena)
if (type == mps_message_type_gc_start()) {
nCollsStart += 1;
- printf("\n{\n Collection %d started. Because:\n", nCollsStart);
+ printf("\n{\n Collection %lu started. Because:\n", nCollsStart);
printf(" %s\n", mps_message_gc_start_why(arena, message));
printf(" clock: %"PRIuLONGEST"\n", (ulongest_t)mps_message_clock(arena, message));
@@ -75,25 +74,12 @@ static void report(mps_arena_t arena)
condemned = mps_message_gc_condemned_size(arena, message);
not_condemned = mps_message_gc_not_condemned_size(arena, message);
- printf("\n Collection %d finished:\n", nCollsDone);
+ printf("\n Collection %lu finished:\n", nCollsDone);
printf(" live %"PRIuLONGEST"\n", (ulongest_t)live);
printf(" condemned %"PRIuLONGEST"\n", (ulongest_t)condemned);
printf(" not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned);
printf(" clock: %"PRIuLONGEST"\n", (ulongest_t)mps_message_clock(arena, message));
printf("}\n");
-
- if(condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024) {
- /* When condemned size is larger than could happen in a gen 2
- * collection (discounting ramps, natch), guess that was a dynamic
- * collection, and reset the commit limit, so it doesn't run out.
- *
- * GDR 2013-03-12: Fiddling with the commit limit was causing
- * the test to fail sometimes (see job003440), so I've commented
- * out this feature.
- */
- /* die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit"); */
- }
-
} else {
cdie(0, "unknown message type");
break;
@@ -101,25 +87,25 @@ static void report(mps_arena_t arena)
mps_message_discard(arena, message);
}
-
- return;
}
/* make -- create one new object */
-static mps_addr_t make(void)
+static mps_addr_t make(size_t rootsCount)
{
- size_t length = rnd() % (2*avLEN);
+ static unsigned long calls = 0;
+ size_t length = rnd() % (scale * avLEN);
size_t size = (length+2) * sizeof(mps_word_t);
mps_addr_t p;
mps_res_t res;
+ ++ calls;
do {
MPS_RESERVE_BLOCK(res, p, ap, size);
if (res)
die(res, "MPS_RESERVE_BLOCK");
- res = dylan_init(p, size, exactRoots, exactRootsCOUNT);
+ res = dylan_init(p, size, exactRoots, rootsCount);
if (res)
die(res, "dylan_init");
} while(!mps_commit(ap, p, size));
@@ -141,7 +127,8 @@ static void test_stepper(mps_addr_t object, mps_fmt_t fmt, mps_pool_t pool,
/* test -- the body of the test */
-static void test(mps_arena_t arena)
+static void test(mps_arena_t arena, mps_pool_class_t pool_class,
+ size_t roots_count)
{
mps_fmt_t format;
mps_chain_t chain;
@@ -153,11 +140,12 @@ static void test(mps_arena_t arena)
mps_ap_t busy_ap;
mps_addr_t busy_init;
mps_pool_t pool;
+ int described = 0;
die(dylan_fmt(&format, arena), "fmt_create");
die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create");
- die(mps_pool_create(&pool, arena, mps_class_amc(), format, chain),
+ die(mps_pool_create(&pool, arena, pool_class, format, chain),
"pool_create(amc)");
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
@@ -181,6 +169,8 @@ static void test(mps_arena_t arena)
/* create an ap, and leave it busy */
die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy");
+ nCollsStart = 0;
+ nCollsDone = 0;
collections = 0;
rampSwitch = rampSIZE;
die(mps_ap_alloc_pattern_begin(ap, ramp), "pattern begin (ap)");
@@ -188,15 +178,18 @@ static void test(mps_arena_t arena)
ramping = 1;
objs = 0;
while (collections < collectionsCOUNT) {
- mps_word_t c;
size_t r;
- c = mps_collections(arena);
- if (collections != c) {
- collections = c;
- report(arena);
+ report(arena);
+ if (collections != nCollsStart) {
+ if (!described) {
+ die(ArenaDescribe(arena, mps_lib_get_stdout(), 0), "ArenaDescribe");
+ described = TRUE;
+ }
+ collections = nCollsStart;
- printf("%lu objects (mps_collections says: %lu)\n", objs, c);
+ printf("%lu objects (nCollsStart=%"PRIuLONGEST")\n", objs,
+ (ulongest_t)collections);
/* test mps_arena_has_addr */
{
@@ -268,13 +261,13 @@ static void test(mps_arena_t arena)
i = (r >> 1) % exactRootsCOUNT;
if (exactRoots[i] != objNULL)
cdie(dylan_check(exactRoots[i]), "dying root check");
- exactRoots[i] = make();
+ exactRoots[i] = make(roots_count);
if (exactRoots[(exactRootsCOUNT-1) - i] != objNULL)
dylan_write(exactRoots[(exactRootsCOUNT-1) - i],
exactRoots, exactRootsCOUNT);
} else {
i = (r >> 1) % ambigRootsCOUNT;
- ambigRoots[(ambigRootsCOUNT-1) - i] = make();
+ ambigRoots[(ambigRootsCOUNT-1) - i] = make(roots_count);
/* Create random interior pointers */
ambigRoots[i] = (mps_addr_t)((char *)(ambigRoots[i/2]) + 1);
}
@@ -285,13 +278,14 @@ static void test(mps_arena_t arena)
if (objs % 1024 == 0) {
report(arena);
putchar('.');
- fflush(stdout);
+ (void)fflush(stdout);
}
++objs;
}
(void)mps_commit(busy_ap, busy_init, 64);
+ mps_arena_park(arena);
mps_ap_destroy(busy_ap);
mps_ap_destroy(ap);
mps_root_destroy(exactRoot);
@@ -299,27 +293,33 @@ static void test(mps_arena_t arena)
mps_pool_destroy(pool);
mps_chain_destroy(chain);
mps_fmt_destroy(format);
+ mps_arena_release(arena);
}
int main(int argc, char *argv[])
{
+ size_t i, grainSize;
mps_arena_t arena;
mps_thr_t thread;
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
-
- die(mps_arena_create(&arena, mps_arena_class_vm(), 2*testArenaSIZE),
- "arena_create");
+ testlib_init(argc, argv);
+
+ scale = (size_t)1 << (rnd() % 6);
+ for (i = 0; i < genCOUNT; ++i) testChain[i].mps_capacity *= scale;
+ grainSize = rnd_grain(scale * testArenaSIZE);
+ printf("Picked scale=%lu grainSize=%lu\n", (unsigned long)scale, (unsigned long)grainSize);
+
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, scale * testArenaSIZE);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, grainSize);
+ MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, scale * testArenaSIZE);
+ die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create");
+ } MPS_ARGS_END(args);
mps_message_type_enable(arena, mps_message_type_gc());
mps_message_type_enable(arena, mps_message_type_gc_start());
- /* GDR 2013-03-12: Fiddling with the commit limit was causing
- * the test to fail sometimes (see job003440), so I've commented
- * out this feature.
- */
- /*die(mps_arena_commit_limit_set(arena, testArenaSIZE), "set limit");*/
die(mps_thread_reg(&thread, arena), "thread_reg");
- test(arena);
+ test(arena, mps_class_amc(), exactRootsCOUNT);
+ test(arena, mps_class_amcz(), 0);
mps_thread_dereg(thread);
report(arena);
mps_arena_destroy(arena);
@@ -331,7 +331,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/amcsshe.c b/code/amcsshe.c
index de8eec82a3..bd3ea73d06 100644
--- a/code/amcsshe.c
+++ b/code/amcsshe.c
@@ -1,7 +1,7 @@
/* amcsshe.c: POOL CLASS AMC STRESS TEST WITH HEADER
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (c) 2002 Global Graphics Software.
*/
@@ -12,18 +12,15 @@
#include "mpscamc.h"
#include "mpsavm.h"
#include "mpstd.h"
-#ifdef MPS_OS_W3
-#include "mpsw3.h"
-#endif
#include "mps.h"
-#include
-#include
+
+#include /* fflush, printf, putchar */
/* These values have been tuned in the hope of getting one dynamic collection. */
#define headerFACTOR ((float)(20 + headerSIZE) / 20)
/* headerFACTOR measures how much larger objects are compared to fmtdy. */
-#define testArenaSIZE ((size_t)(1000*headerFACTOR)*1024)
+#define testArenaSIZE ((size_t)(2000*headerFACTOR)*1024)
#define gen1SIZE ((size_t)(150*headerFACTOR))
#define gen2SIZE ((size_t)(170*headerFACTOR))
#define avLEN 3
@@ -51,7 +48,7 @@ static mps_addr_t exactRoots[exactRootsCOUNT];
static mps_addr_t ambigRoots[ambigRootsCOUNT];
static mps_addr_t bogusRoots[bogusRootsCOUNT];
-static mps_addr_t make(void)
+static mps_addr_t make(size_t roots_count)
{
size_t length = rnd() % (2*avLEN);
size_t size = (length+2) * sizeof(mps_word_t);
@@ -63,7 +60,7 @@ static mps_addr_t make(void)
if (res)
die(res, "MPS_RESERVE_BLOCK");
userP = (mps_addr_t)((char*)p + headerSIZE);
- res = dylan_init(userP, size, exactRoots, exactRootsCOUNT);
+ res = dylan_init(userP, size, exactRoots, roots_count);
if (res)
die(res, "dylan_init");
((int*)p)[0] = realHeader;
@@ -94,27 +91,15 @@ static void report(mps_arena_t arena)
printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned);
mps_message_discard(arena, message);
-
- if (condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024) {
- /* When condemned size is larger than could happen in a gen 2
- * collection (discounting ramps, natch), guess that was a dynamic
- * collection, and reset the commit limit, so it doesn't run out.
- *
- * GDR 2013-03-07: Fiddling with the commit limit was causing
- * the test to fail sometimes (see job003432), so I've commented
- * out this feature.
- */
- /*die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit");*/
- }
}
}
/* test -- the body of the test */
-static void *test(void *arg, size_t s)
+static void *test(mps_arena_t arena, mps_pool_class_t pool_class,
+ size_t roots_count)
{
- mps_arena_t arena;
mps_fmt_t format;
mps_chain_t chain;
mps_root_t exactRoot, ambigRoot, bogusRoot;
@@ -125,13 +110,10 @@ static void *test(void *arg, size_t s)
mps_ap_t busy_ap;
mps_addr_t busy_init;
- arena = (mps_arena_t)arg;
- (void)s; /* unused */
-
die(EnsureHeaderFormat(&format, arena), "fmt_create");
die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create");
- die(mps_pool_create(&pool, arena, mps_class_amc(), format, chain),
+ die(mps_pool_create(&pool, arena, pool_class, format, chain),
"pool_create(amc)");
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
@@ -173,7 +155,8 @@ static void *test(void *arg, size_t s)
if (collections != c) {
collections = c;
- printf("\nCollection %lu, %lu objects.\n", c, objs);
+ printf("\nCollection %"PRIuLONGEST", %lu objects.\n",
+ (ulongest_t)c, objs);
report(arena);
for (r = 0; r < exactRootsCOUNT; ++r) {
if (exactRoots[r] != objNULL)
@@ -219,13 +202,13 @@ static void *test(void *arg, size_t s)
i = (r >> 1) % exactRootsCOUNT;
if (exactRoots[i] != objNULL)
die(HeaderFormatCheck(exactRoots[i]), "wrapper check");
- exactRoots[i] = make();
+ exactRoots[i] = make(roots_count);
if (exactRoots[(exactRootsCOUNT-1) - i] != objNULL)
dylan_write(exactRoots[(exactRootsCOUNT-1) - i],
exactRoots, exactRootsCOUNT);
} else {
i = (r >> 1) % ambigRootsCOUNT;
- ambigRoots[(ambigRootsCOUNT-1) - i] = make();
+ ambigRoots[(ambigRootsCOUNT-1) - i] = make(roots_count);
/* Create random interior pointers */
ambigRoots[i] = (mps_addr_t)((char *)(ambigRoots[i/2]) + 1);
}
@@ -236,13 +219,14 @@ static void *test(void *arg, size_t s)
if (objs % 1024 == 0) {
report(arena);
putchar('.');
- fflush(stdout);
+ (void)fflush(stdout);
}
++objs;
}
(void)mps_commit(busy_ap, busy_init, 64);
+ mps_arena_park(arena);
mps_ap_destroy(busy_ap);
mps_ap_destroy(ap);
mps_root_destroy(exactRoot);
@@ -251,6 +235,7 @@ static void *test(void *arg, size_t s)
mps_pool_destroy(pool);
mps_chain_destroy(chain);
mps_fmt_destroy(format);
+ mps_arena_release(arena);
return NULL;
}
@@ -260,21 +245,19 @@ int main(int argc, char *argv[])
{
mps_arena_t arena;
mps_thr_t thread;
- void *r;
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
+ testlib_init(argc, argv);
- die(mps_arena_create(&arena, mps_arena_class_vm(), 3*testArenaSIZE),
- "arena_create\n");
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE));
+ MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, testArenaSIZE);
+ die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create");
+ } MPS_ARGS_END(args);
mps_message_type_enable(arena, mps_message_type_gc());
- /* GDR 2013-03-07: Fiddling with the commit limit was causing
- * the test to fail sometimes (see job003432), so I've commented
- * out this feature.
- */
- /*die(mps_arena_commit_limit_set(arena, testArenaSIZE), "set limit");*/
die(mps_thread_reg(&thread, arena), "thread_reg");
- mps_tramp(&r, test, arena, 0);
+ test(arena, mps_class_amc(), exactRootsCOUNT);
+ test(arena, mps_class_amcz(), 0);
mps_thread_dereg(thread);
mps_arena_destroy(arena);
@@ -285,7 +268,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/amcssth.c b/code/amcssth.c
index e7f8f88233..3eea28df07 100644
--- a/code/amcssth.c
+++ b/code/amcssth.c
@@ -1,24 +1,37 @@
/* amcssth.c: POOL CLASS AMC STRESS TEST WITH TWO THREADS
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (c) 2002 Global Graphics Software.
*
- * .posix: This is Posix only.
+ * .mode: This test case has two modes:
+ *
+ * .mode.walk: In this mode, the main thread parks the arena half way
+ * through the test case and runs mps_arena_formatted_objects_walk().
+ * This checks that walking works while the other threads continue to
+ * allocate in the background.
+ *
+ * .mode.commit: In this mode, the arena's commit limit is set. This
+ * checks that the MPS can make progress inside a tight limit in the
+ * presence of allocation on multiple threads. But this is
+ * incompatible with .mode.walk: if the arena is parked, then the
+ * arena has no chance to make progress.
*/
-#define _POSIX_C_SOURCE 199309L
-
#include "fmtdy.h"
#include "fmtdytst.h"
#include "testlib.h"
+#include "testthr.h"
#include "mpslib.h"
#include "mpscamc.h"
#include "mpsavm.h"
-#include
-#include
-#include
-#include
+
+#include /* fflush, printf, putchar */
+
+enum {
+ ModeWALK = 0, /* .mode.walk */
+ ModeCOMMIT = 1 /* .mode.commit */
+};
/* These values have been tuned in the hope of getting one dynamic collection. */
@@ -43,50 +56,19 @@ static mps_gen_param_s testChain[genCOUNT] = {
#define objNULL ((mps_addr_t)MPS_WORD_CONST(0xDECEA5ED))
-static mps_pool_t pool;
static mps_addr_t exactRoots[exactRootsCOUNT];
static mps_addr_t ambigRoots[ambigRootsCOUNT];
-/* report - report statistics from any terminated GCs */
-
-static void report(mps_arena_t arena)
-{
- mps_message_t message;
- static int nCollections = 0;
-
- while (mps_message_get(&message, arena, mps_message_type_gc())) {
- size_t live, condemned, not_condemned;
-
- live = mps_message_gc_live_size(arena, message);
- condemned = mps_message_gc_condemned_size(arena, message);
- not_condemned = mps_message_gc_not_condemned_size(arena, message);
-
- printf("\nCollection %d finished:\n", ++nCollections);
- printf("live %"PRIuLONGEST"\n", (ulongest_t)live);
- printf("condemned %"PRIuLONGEST"\n", (ulongest_t)condemned);
- printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned);
-
- mps_message_discard(arena, message);
-
- if (condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024)
- /* When condemned size is larger than could happen in a gen 2
- * collection (discounting ramps, natch), guess that was a dynamic
- * collection, and reset the commit limit, so it doesn't run out. */
- die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit");
- }
-}
-
-mps_arena_t arena;
-mps_fmt_t format;
-mps_chain_t chain;
-mps_root_t exactRoot, ambigRoot;
-unsigned long objs = 0;
+static mps_word_t collections;
+static mps_arena_t arena;
+static mps_root_t exactRoot, ambigRoot;
+static unsigned long objs = 0;
/* make -- create one new object */
-static mps_addr_t make(mps_ap_t ap)
+static mps_addr_t make(mps_ap_t ap, size_t roots_count)
{
size_t length = rnd() % (2*avLEN);
size_t size = (length+2) * sizeof(mps_word_t);
@@ -97,7 +79,7 @@ static mps_addr_t make(mps_ap_t ap)
MPS_RESERVE_BLOCK(res, p, ap, size);
if (res)
die(res, "MPS_RESERVE_BLOCK");
- res = dylan_init(p, size, exactRoots, exactRootsCOUNT);
+ res = dylan_init(p, size, exactRoots, roots_count);
if (res)
die(res, "dylan_init");
} while(!mps_commit(ap, p, size));
@@ -108,59 +90,18 @@ static mps_addr_t make(mps_ap_t ap)
/* test_stepper -- stepping function for walk */
-static void test_stepper(mps_addr_t object, mps_fmt_t fmt, mps_pool_t pol,
+static void test_stepper(mps_addr_t object, mps_fmt_t fmt, mps_pool_t pool,
void *p, size_t s)
{
- testlib_unused(object); testlib_unused(fmt); testlib_unused(pol);
+ testlib_unused(object); testlib_unused(fmt); testlib_unused(pool);
testlib_unused(s);
(*(unsigned long *)p)++;
}
-/* init -- initialize pool and roots */
-
-static void init(void)
-{
- size_t i;
-
- die(dylan_fmt(&format, arena), "fmt_create");
- die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create");
-
- die(mps_pool_create(&pool, arena, mps_class_amc(), format, chain),
- "pool_create(amc)");
-
- for(i = 0; i < exactRootsCOUNT; ++i)
- exactRoots[i] = objNULL;
- for(i = 0; i < ambigRootsCOUNT; ++i)
- ambigRoots[i] = rnd_addr();
-
- die(mps_root_create_table_masked(&exactRoot, arena,
- mps_rank_exact(), (mps_rm_t)0,
- &exactRoots[0], exactRootsCOUNT,
- (mps_word_t)1),
- "root_create_table(exact)");
- die(mps_root_create_table(&ambigRoot, arena,
- mps_rank_ambig(), (mps_rm_t)0,
- &ambigRoots[0], ambigRootsCOUNT),
- "root_create_table(ambig)");
-}
-
-
-/* finish -- finish pool and roots */
-
-static void finish(void)
-{
- mps_root_destroy(exactRoot);
- mps_root_destroy(ambigRoot);
- mps_pool_destroy(pool);
- mps_chain_destroy(chain);
- mps_fmt_destroy(format);
-}
-
-
/* churn -- create an object and install into roots */
-static void churn(mps_ap_t ap)
+static void churn(mps_ap_t ap, size_t roots_count)
{
size_t i;
size_t r;
@@ -171,32 +112,73 @@ static void churn(mps_ap_t ap)
i = (r >> 1) % exactRootsCOUNT;
if (exactRoots[i] != objNULL)
cdie(dylan_check(exactRoots[i]), "dying root check");
- exactRoots[i] = make(ap);
+ exactRoots[i] = make(ap, roots_count);
if (exactRoots[(exactRootsCOUNT-1) - i] != objNULL)
dylan_write(exactRoots[(exactRootsCOUNT-1) - i],
exactRoots, exactRootsCOUNT);
} else {
i = (r >> 1) % ambigRootsCOUNT;
- ambigRoots[(ambigRootsCOUNT-1) - i] = make(ap);
+ ambigRoots[(ambigRootsCOUNT-1) - i] = make(ap, roots_count);
/* Create random interior pointers */
ambigRoots[i] = (mps_addr_t)((char *)(ambigRoots[i/2]) + 1);
}
}
+typedef struct closure_s {
+ mps_pool_t pool;
+ size_t roots_count;
+} closure_s, *closure_t;
+
+static void *kid_thread(void *arg)
+{
+ void *marker = ▮
+ mps_thr_t thread;
+ mps_root_t reg_root;
+ mps_ap_t ap;
+ closure_t cl = arg;
+
+ die(mps_thread_reg(&thread, (mps_arena_t)arena), "thread_reg");
+ die(mps_root_create_reg(®_root, arena, mps_rank_ambig(), 0, thread,
+ mps_stack_scan_ambig, marker, 0), "root_create");
+
+ die(mps_ap_create(&ap, cl->pool, mps_rank_exact()), "BufferCreate(fooey)");
+ while(mps_collections(arena) < collectionsCOUNT) {
+ churn(ap, cl->roots_count);
+ }
+ mps_ap_destroy(ap);
+
+ mps_root_destroy(reg_root);
+ mps_thread_dereg(thread);
+
+ return NULL;
+}
+
+
/* test -- the body of the test */
-static void *test(void *arg, size_t s)
+static void test_pool(const char *name, mps_pool_t pool, size_t roots_count,
+ int mode)
{
size_t i;
- mps_word_t collections, rampSwitch;
+ mps_word_t rampSwitch;
mps_alloc_pattern_t ramp = mps_alloc_pattern_ramp();
int ramping;
mps_ap_t ap, busy_ap;
mps_addr_t busy_init;
+ testthr_t kids[10];
+ closure_s cl;
+ int walked = FALSE, ramped = FALSE;
- arena = (mps_arena_t)arg;
- (void)s; /* unused */
+ printf("\n------ mode: %s pool: %s-------\n",
+ mode == ModeWALK ? "WALK" : "COMMIT", name);
+
+ cl.pool = pool;
+ cl.roots_count = roots_count;
+ collections = 0;
+
+ for (i = 0; i < NELEMS(kids); ++i)
+ testthr_create(&kids[i], kid_thread, &cl);
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
die(mps_ap_create(&busy_ap, pool, mps_rank_exact()), "BufferCreate 2");
@@ -204,69 +186,85 @@ static void *test(void *arg, size_t s)
/* create an ap, and leave it busy */
die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy");
- collections = 0;
rampSwitch = rampSIZE;
die(mps_ap_alloc_pattern_begin(ap, ramp), "pattern begin (ap)");
die(mps_ap_alloc_pattern_begin(busy_ap, ramp), "pattern begin (busy_ap)");
ramping = 1;
while (collections < collectionsCOUNT) {
- unsigned long c;
- size_t r;
-
- c = mps_collections(arena);
-
- if (collections != c) {
- collections = c;
- printf("\nCollection %lu started, %lu objects.\n", c, objs);
- report(arena);
-
- for (i = 0; i < exactRootsCOUNT; ++i)
- cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]),
- "all roots check");
-
- if (collections == collectionsCOUNT / 2) {
- unsigned long object_count = 0;
- mps_arena_park(arena);
- mps_arena_formatted_objects_walk(arena, test_stepper, &object_count, 0);
- mps_arena_release(arena);
- printf("stepped on %lu objects.\n", object_count);
- }
- if (collections == rampSwitch) {
- int begin_ramp = !ramping
- || /* Every other time, switch back immediately. */ (collections & 1);
-
- rampSwitch += rampSIZE;
- if (ramping) {
- die(mps_ap_alloc_pattern_end(ap, ramp), "pattern end (ap)");
- die(mps_ap_alloc_pattern_end(busy_ap, ramp), "pattern end (busy_ap)");
- ramping = 0;
- /* kill half of the roots */
- for(i = 0; i < exactRootsCOUNT; i += 2) {
- if (exactRoots[i] != objNULL) {
- cdie(dylan_check(exactRoots[i]), "ramp kill check");
- exactRoots[i] = objNULL;
+ mps_message_type_t type;
+
+ if (mps_message_queue_type(&type, arena)) {
+ mps_message_t msg;
+ mps_bool_t b = mps_message_get(&msg, arena, type);
+ Insist(b); /* we just checked there was one */
+
+ if (type == mps_message_type_gc()) {
+ size_t live = mps_message_gc_live_size(arena, msg);
+ size_t condemned = mps_message_gc_condemned_size(arena, msg);
+ size_t not_condemned = mps_message_gc_not_condemned_size(arena, msg);
+
+ printf("\nCollection %lu finished:\n", collections++);
+ printf("live %"PRIuLONGEST"\n", (ulongest_t)live);
+ printf("condemned %"PRIuLONGEST"\n", (ulongest_t)condemned);
+ printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned);
+
+ } else if (type == mps_message_type_gc_start()) {
+ printf("\nCollection %lu started, %lu objects, committed=%lu.\n",
+ (unsigned long)collections, objs,
+ (unsigned long)mps_arena_committed(arena));
+
+ for (i = 0; i < exactRootsCOUNT; ++i)
+ cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]),
+ "all roots check");
+
+ if (mode == ModeWALK && collections >= collectionsCOUNT / 2 && !walked)
+ {
+ unsigned long count = 0;
+ mps_arena_park(arena);
+ mps_arena_formatted_objects_walk(arena, test_stepper, &count, 0);
+ mps_arena_release(arena);
+ printf("stepped on %lu objects.\n", count);
+ walked = TRUE;
+ }
+ if (collections >= rampSwitch && !ramped) {
+ /* Every other time, switch back immediately. */
+ int begin_ramp = !ramping || (collections & 1);
+
+ rampSwitch += rampSIZE;
+ if (ramping) {
+ die(mps_ap_alloc_pattern_end(ap, ramp), "pattern end (ap)");
+ die(mps_ap_alloc_pattern_end(busy_ap, ramp),
+ "pattern end (busy_ap)");
+ ramping = 0;
+ /* kill half of the roots */
+ for(i = 0; i < exactRootsCOUNT; i += 2) {
+ if (exactRoots[i] != objNULL) {
+ cdie(dylan_check(exactRoots[i]), "ramp kill check");
+ exactRoots[i] = objNULL;
+ }
}
}
+ if (begin_ramp) {
+ die(mps_ap_alloc_pattern_begin(ap, ramp),
+ "pattern rebegin (ap)");
+ die(mps_ap_alloc_pattern_begin(busy_ap, ramp),
+ "pattern rebegin (busy_ap)");
+ ramping = 1;
+ }
}
- if (begin_ramp) {
- die(mps_ap_alloc_pattern_begin(ap, ramp),
- "pattern rebegin (ap)");
- die(mps_ap_alloc_pattern_begin(busy_ap, ramp),
- "pattern rebegin (busy_ap)");
- ramping = 1;
- }
+ ramped = TRUE;
}
- }
-
- churn(ap);
- r = (size_t)rnd();
-
- if (r % initTestFREQ == 0)
- *(int*)busy_init = -1; /* check that the buffer is still there */
+ mps_message_discard(arena, msg);
+ }
+ churn(ap, roots_count);
+ {
+ size_t r = (size_t)rnd();
+ if (r % initTestFREQ == 0)
+ *(int*)busy_init = -1; /* check that the buffer is still there */
+ }
if (objs % 1024 == 0) {
- report(arena);
putchar('.');
fflush(stdout);
}
@@ -276,80 +274,76 @@ static void *test(void *arg, size_t s)
mps_ap_destroy(busy_ap);
mps_ap_destroy(ap);
- return NULL;
+ for (i = 0; i < NELEMS(kids); ++i)
+ testthr_join(&kids[i], NULL);
}
-
-static void *fooey2(void *arg, size_t s)
+static void test_arena(int mode)
{
- mps_ap_t ap;
+ size_t i;
+ mps_fmt_t format;
+ mps_chain_t chain;
+ mps_thr_t thread;
+ mps_root_t reg_root;
+ mps_pool_t amc_pool, amcz_pool;
+ void *marker = ▮
- (void)arg; (void)s; /* unused */
- die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate(fooey)");
- while(mps_collections(arena) < collectionsCOUNT) {
- churn(ap);
- }
- mps_ap_destroy(ap);
- return NULL;
-}
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE));
+ if (mode == ModeCOMMIT)
+ MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, 2 * testArenaSIZE);
+ die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create");
+ } MPS_ARGS_END(args);
+ mps_message_type_enable(arena, mps_message_type_gc());
+ mps_message_type_enable(arena, mps_message_type_gc_start());
+ die(dylan_fmt(&format, arena), "fmt_create");
+ die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create");
-static void *fooey(void* childIsFinishedReturn)
-{
- void *r;
- mps_thr_t thread;
- void *marker = ▮
- mps_root_t reg_root;
+ for(i = 0; i < exactRootsCOUNT; ++i)
+ exactRoots[i] = objNULL;
+ for(i = 0; i < ambigRootsCOUNT; ++i)
+ ambigRoots[i] = rnd_addr();
- die(mps_thread_reg(&thread, (mps_arena_t)arena), "thread_reg");
+ die(mps_root_create_table_masked(&exactRoot, arena,
+ mps_rank_exact(), (mps_rm_t)0,
+ &exactRoots[0], exactRootsCOUNT,
+ (mps_word_t)1),
+ "root_create_table(exact)");
+ die(mps_root_create_table(&ambigRoot, arena,
+ mps_rank_ambig(), (mps_rm_t)0,
+ &ambigRoots[0], ambigRootsCOUNT),
+ "root_create_table(ambig)");
+ die(mps_thread_reg(&thread, arena), "thread_reg");
die(mps_root_create_reg(®_root, arena, mps_rank_ambig(), 0, thread,
mps_stack_scan_ambig, marker, 0), "root_create");
- mps_tramp(&r, fooey2, NULL, 0);
- mps_root_destroy(reg_root);
- mps_thread_dereg(thread);
- *(int *)childIsFinishedReturn = 1;
- return r;
-}
+ die(mps_pool_create(&amc_pool, arena, mps_class_amc(), format, chain),
+ "pool_create(amc)");
+ die(mps_pool_create(&amcz_pool, arena, mps_class_amcz(), format, chain),
+ "pool_create(amcz)");
-int main(int argc, char *argv[])
-{
- mps_thr_t thread;
- mps_root_t reg_root;
- void *marker = ▮
- pthread_t kids[10];
- unsigned i;
- void *r;
- int childIsFinished = 0;
-
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
+ test_pool("AMC", amc_pool, exactRootsCOUNT, mode);
+ test_pool("AMCZ", amcz_pool, 0, mode);
- die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
- "arena_create");
- mps_message_type_enable(arena, mps_message_type_gc());
- init();
- die(mps_thread_reg(&thread, arena), "thread_reg");
- die(mps_root_create_reg(®_root, arena, mps_rank_ambig(), 0, thread,
- mps_stack_scan_ambig, marker, 0), "root_create");
- for (i = 0; i < sizeof(kids)/sizeof(kids[0]); ++i) {
- int err = pthread_create(&kids[i], NULL, fooey, (void *)&childIsFinished);
- if (err != 0)
- error("pthread_create returned %d", err);
- }
- mps_tramp(&r, test, arena, 0);
+ mps_arena_park(arena);
+ mps_pool_destroy(amc_pool);
+ mps_pool_destroy(amcz_pool);
mps_root_destroy(reg_root);
mps_thread_dereg(thread);
-
- for (i = 0; i < sizeof(kids)/sizeof(kids[0]); ++i) {
- int err = pthread_join(kids[i], NULL);
- if (err != 0)
- error("pthread_join returned %d", err);
- }
-
- finish();
- report(arena);
+ mps_root_destroy(exactRoot);
+ mps_root_destroy(ambigRoot);
+ mps_chain_destroy(chain);
+ mps_fmt_destroy(format);
mps_arena_destroy(arena);
+}
+
+int main(int argc, char *argv[])
+{
+ testlib_init(argc, argv);
+ test_arena(ModeWALK);
+ test_arena(ModeCOMMIT);
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
return 0;
@@ -358,21 +352,21 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 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
@@ -383,7 +377,7 @@ int main(int argc, char *argv[])
* 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
diff --git a/code/amsss.c b/code/amsss.c
index 43d2541f40..223bd205ae 100644
--- a/code/amsss.c
+++ b/code/amsss.c
@@ -1,7 +1,7 @@
/* amsss.c: POOL CLASS AMS STRESS TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (c) 2002 Global Graphics Software.
*
* .design: Adapted from amcss.c, but not counting collections, just
@@ -15,14 +15,10 @@
#include "mpscams.h"
#include "mpsavm.h"
#include "mpstd.h"
-#ifdef MPS_OS_W3
-#include "mpsw3.h"
-#endif
#include "mps.h"
-#include
-#include
-#include
-#include
+#include "mpm.h"
+
+#include /* fflush, printf */
#define exactRootsCOUNT 50
@@ -32,7 +28,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 } };
@@ -110,9 +106,10 @@ static mps_addr_t make(void)
/* test -- the actual stress test */
static mps_pool_debug_option_s freecheckOptions =
- { NULL, 0, (const void *)"Dead", 4 };
+ { NULL, 0, "Dead", 4 };
-static void *test(void *arg, size_t haveAmbigous)
+static void test_pool(mps_pool_class_t pool_class, mps_arg_s args[],
+ mps_bool_t haveAmbiguous)
{
mps_pool_t pool;
mps_root_t exactRoot, ambigRoot = NULL;
@@ -121,14 +118,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();
@@ -137,7 +133,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),
@@ -146,20 +142,22 @@ static void *test(void *arg, size_t haveAmbigous)
/* create an ap, and leave it busy */
die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy");
+ die(PoolDescribe(pool, mps_lib_get_stdout(), 0), "PoolDescribe");
+
objs = 0; totalSize = 0;
while(totalSize < totalSizeMAX) {
if (totalSize > lastStep + totalSizeSTEP) {
lastStep = totalSize;
printf("\nSize %"PRIuLONGEST" bytes, %lu objects.\n",
(ulongest_t)totalSize, objs);
- fflush(stdout);
+ (void)fflush(stdout);
for(i = 0; i < exactRootsCOUNT; ++i)
cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]),
"all roots check");
}
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");
@@ -184,7 +182,7 @@ static void *test(void *arg, size_t haveAmbigous)
if (objs % 256 == 0) {
printf(".");
report();
- fflush(stdout);
+ (void)fflush(stdout);
}
}
@@ -192,82 +190,54 @@ 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;
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
+ testlib_init(argc, argv);
+
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE));
+ MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, 2 * testArenaSIZE);
+ die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create");
+ } MPS_ARGS_END(args);
- die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
- "arena_create");
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 (ownChain)
+ 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, ambig);
+ } MPS_ARGS_END(args);
+ }
+ mps_arena_park(arena);
mps_chain_destroy(chain);
mps_fmt_destroy(format);
mps_thread_dereg(thread);
@@ -280,7 +250,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/amssshe.c b/code/amssshe.c
index ddbda60701..26dc25822e 100644
--- a/code/amssshe.c
+++ b/code/amssshe.c
@@ -1,7 +1,7 @@
/* amssshe.c: POOL CLASS AMS STRESS TEST WITH HEADERS
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .design: Adapted from amsss.c.
*/
@@ -13,14 +13,9 @@
#include "mpscams.h"
#include "mpsavm.h"
#include "mpstd.h"
-#ifdef MPS_OS_W3
-#include "mpsw3.h"
-#endif
#include "mps.h"
-#include
-#include
-#include
-#include
+
+#include /* fflush, printf */
#define exactRootsCOUNT 50
@@ -111,7 +106,7 @@ static void *test(void *arg, size_t s)
lastStep = totalSize;
printf("\nSize %"PRIuLONGEST" bytes, %lu objects.\n",
(ulongest_t)totalSize, objs);
- fflush(stdout);
+ (void)fflush(stdout);
for(i = 0; i < exactRootsCOUNT; ++i)
cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]),
"all roots check");
@@ -139,11 +134,12 @@ static void *test(void *arg, size_t s)
++objs;
if (objs % 256 == 0) {
printf(".");
- fflush(stdout);
+ (void)fflush(stdout);
}
}
(void)mps_commit(busy_ap, busy_init, 64);
+ mps_arena_park(arena);
mps_ap_destroy(busy_ap);
mps_ap_destroy(ap);
mps_root_destroy(exactRoot);
@@ -151,6 +147,7 @@ static void *test(void *arg, size_t s)
mps_pool_destroy(pool);
mps_chain_destroy(chain);
mps_fmt_destroy(format);
+ mps_arena_release(arena);
return NULL;
}
@@ -162,11 +159,13 @@ int main(int argc, char *argv[])
mps_thr_t thread;
void *r;
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
+ testlib_init(argc, argv);
- die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
- "arena_create");
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE));
+ die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create");
+ } MPS_ARGS_END(args);
die(mps_thread_reg(&thread, arena), "thread_reg");
mps_tramp(&r, test, arena, 0);
mps_thread_dereg(thread);
@@ -179,7 +178,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/anangc.gmk b/code/anangc.gmk
new file mode 100644
index 0000000000..f0a7d2ff51
--- /dev/null
+++ b/code/anangc.gmk
@@ -0,0 +1,66 @@
+# -*- makefile -*-
+#
+# anangc.gmk: BUILD FOR ANSI/ANSI/GCC PLATFORM
+#
+# $Id$
+# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
+
+PFM = anangc
+
+MPMPF = \
+ lockan.c \
+ prmcan.c \
+ protan.c \
+ span.c \
+ ssan.c \
+ than.c \
+ vman.c
+
+LIBS = -lm -lpthread
+
+include gc.gmk
+
+CFLAGSCOMPILER += -DCONFIG_PF_ANSI -DCONFIG_THREAD_SINGLE
+
+include comm.gmk
+
+
+# C. COPYRIGHT AND LICENSE
+#
+# Copyright (C) 2001-2014 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.
diff --git a/code/ananll.gmk b/code/ananll.gmk
new file mode 100644
index 0000000000..cc95645f21
--- /dev/null
+++ b/code/ananll.gmk
@@ -0,0 +1,66 @@
+# -*- makefile -*-
+#
+# ananll.gmk: BUILD FOR ANSI/ANSI/Clang PLATFORM
+#
+# $Id$
+# Copyright (c) 2014 Ravenbrook Limited. See end of file for license.
+
+PFM = ananll
+
+MPMPF = \
+ lockan.c \
+ prmcan.c \
+ protan.c \
+ span.c \
+ ssan.c \
+ than.c \
+ vman.c
+
+LIBS = -lm -lpthread
+
+include ll.gmk
+
+CFLAGSCOMPILER += -DCONFIG_PF_ANSI -DCONFIG_THREAD_SINGLE
+
+include comm.gmk
+
+
+# C. COPYRIGHT AND LICENSE
+#
+# Copyright (C) 2001-2014 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.
diff --git a/code/ananmv.nmk b/code/ananmv.nmk
new file mode 100644
index 0000000000..41d80a0671
--- /dev/null
+++ b/code/ananmv.nmk
@@ -0,0 +1,62 @@
+# ananmv.nmk: ANSI/ANSI/MICROSOFT VISUAL C/C++ NMAKE FILE -*- makefile -*-
+#
+# $Id$
+# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
+
+PFM = ananmv
+
+PFMDEFS = /DCONFIG_PF_ANSI /DCONFIG_THREAD_SINGLE
+
+MPMPF = \
+ [lockan] \
+ [prmcan] \
+ [protan] \
+ [span] \
+ [ssan] \
+ [than] \
+ [vman]
+
+!INCLUDE commpre.nmk
+!INCLUDE mv.nmk
+!INCLUDE commpost.nmk
+
+
+# C. COPYRIGHT AND LICENSE
+#
+# Copyright (C) 2001-2014 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.
diff --git a/code/apss.c b/code/apss.c
index e395b50df2..5192baada4 100644
--- a/code/apss.c
+++ b/code/apss.c
@@ -1,7 +1,7 @@
/* apss.c: AP MANUAL ALLOC STRESS TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (C) 2002 Global Graphics Software.
*/
@@ -16,7 +16,7 @@
#include "testlib.h"
#include "mpslib.h"
-#include
+#include /* printf */
#include /* malloc */
@@ -41,36 +41,58 @@ static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size)
}
+/* check_allocated_size -- check the allocated size of the pool */
+
+static void check_allocated_size(mps_pool_t pool, mps_ap_t ap, size_t allocated)
+{
+ size_t total_size = mps_pool_total_size(pool);
+ size_t free_size = mps_pool_free_size(pool);
+ size_t ap_free = (size_t)((char *)ap->limit - (char *)ap->init);
+ Insist(total_size - free_size == allocated + ap_free);
+}
+
+
/* stress -- create a pool of the requested type and allocate in it */
-static mps_res_t stress(mps_class_t class, size_t (*size)(unsigned long i),
- mps_arena_t arena, ...)
+static mps_res_t stress(mps_arena_t arena, mps_pool_debug_option_s *options,
+ mps_align_t align,
+ size_t (*size)(size_t i, mps_align_t align),
+ const char *name, mps_pool_class_t pool_class,
+ mps_arg_s args[])
{
mps_res_t res = MPS_RES_OK;
mps_pool_t pool;
mps_ap_t ap;
- va_list arg;
- unsigned long i, k;
+ size_t i, k;
int *ps[testSetSIZE];
size_t ss[testSetSIZE];
+ size_t allocated = 0; /* Total allocated memory */
+ size_t debugOverhead = options ? 2 * alignUp(options->fence_size, align) : 0;
- va_start(arg, arena);
- res = mps_pool_create_v(&pool, arena, class, arg);
- va_end(arg);
- if (res != MPS_RES_OK)
- return res;
+ printf("stress %s\n", name);
+ die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create");
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
/* allocate a load of objects */
for (i=0; i= sizeof(ps[i]))
*ps[i] = 1; /* Write something, so it gets swap. */
+ check_allocated_size(pool, ap, allocated);
+ }
+
+ /* Check introspection functions */
+ for (i = 0; i < NELEMS(ps); ++i) {
+ mps_pool_t addr_pool = NULL;
+ Insist(mps_arena_has_addr(arena, ps[i]));
+ Insist(mps_addr_pool(&addr_pool, arena, ps[i]));
+ Insist(addr_pool == pool);
}
mps_pool_check_fenceposts(pool);
@@ -78,7 +100,7 @@ static mps_res_t stress(mps_class_t class, size_t (*size)(unsigned long i),
for (k=0; k (b)) ? (a) : (b))
-
-#define alignUp(w, a) (((w) + (a) - 1) & ~((size_t)(a) - 1))
-
-
-/* randomSizeAligned -- produce sizes both large and small,
- * aligned by platform alignment */
+/* randomSizeAligned -- produce sizes both large and small, aligned to
+ * align.
+ */
-static size_t randomSizeAligned(unsigned long i)
+static size_t randomSizeAligned(size_t i, mps_align_t align)
{
size_t maxSize = 2 * 160 * 0x2000;
/* Reduce by a factor of 2 every 10 cycles. Total allocation about 40 MB. */
- return alignUp(rnd() % max((maxSize >> (i / 10)), 2) + 1, MPS_PF_ALIGN);
+ return alignUp(rnd() % max((maxSize >> (i / 10)), 2) + 1, align);
}
-static mps_pool_debug_option_s bothOptions8 = {
- /* .fence_template = */ (const void *)"postpost",
- /* .fence_size = */ 8,
- /* .free_template = */ (const void *)"DEAD",
- /* .free_size = */ 4
-};
-
-static mps_pool_debug_option_s bothOptions16 = {
- /* .fence_template = */ (const void *)"postpostpostpost",
- /* .fence_size = */ 16,
- /* .free_template = */ (const void *)"DEAD",
+static mps_pool_debug_option_s bothOptions = {
+ /* .fence_template = */ "post",
+ /* .fence_size = */ 4,
+ /* .free_template = */ "DEAD",
/* .free_size = */ 4
};
static mps_pool_debug_option_s fenceOptions = {
- /* .fence_template = */ (const void *)"\0XXX ''\"\"'' XXX\0",
- /* .fence_size = */ 16,
+ /* .fence_template = */ "123456789abcdef",
+ /* .fence_size = */ 15,
/* .free_template = */ NULL,
/* .free_size = */ 0
};
-/* testInArena -- test all the pool classes in the given arena */
-static void testInArena(mps_arena_t arena, mps_pool_debug_option_s *options)
+/* test -- create arena using given class and arguments; test all the
+ * pool classes in this arena
+ */
+
+static void test(mps_arena_class_t arena_class, mps_arg_s arena_args[],
+ mps_pool_debug_option_s *options)
{
- mps_res_t res;
+ mps_arena_t arena;
+ die(mps_arena_create_k(&arena, arena_class, arena_args), "mps_arena_create");
+
+ MPS_ARGS_BEGIN(args) {
+ mps_align_t align = sizeof(void *) << (rnd() % 4);
+ MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
+ MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE);
+ MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE);
+ MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE);
+ MPS_ARGS_ADD(args, MPS_KEY_SPARE, rnd_double());
+ die(stress(arena, NULL, align, randomSizeAligned, "MVFF",
+ mps_class_mvff(), args), "stress MVFF");
+ } MPS_ARGS_END(args);
/* IWBN to test MVFFDebug, but the MPS doesn't support debugging APs, */
/* yet (MV Debug works here, because it fakes it through PoolAlloc). */
- printf("MVFF\n");
- res = stress(mps_class_mvff(), randomSizeAligned, arena,
- (size_t)65536, (size_t)32, (mps_align_t)MPS_PF_ALIGN, TRUE, TRUE, TRUE);
- if (res == MPS_RES_COMMIT_LIMIT) return;
- die(res, "stress MVFF");
-
- printf("MV debug\n");
- res = stress(mps_class_mv_debug(), randomSizeAligned, arena,
- options, (size_t)65536, (size_t)32, (size_t)65536);
- if (res == MPS_RES_COMMIT_LIMIT) return;
- die(res, "stress MV debug");
-
- printf("MV\n");
- res = stress(mps_class_mv(), randomSizeAligned, arena,
- (size_t)65536, (size_t)32, (size_t)65536);
- if (res == MPS_RES_COMMIT_LIMIT) return;
- die(res, "stress MV");
-
- printf("MVT\n");
- res = stress(mps_class_mvt(), randomSizeAligned, arena,
- (size_t)8, (size_t)32, (size_t)65536, (mps_word_t)4,
- (mps_word_t)50);
- if (res == MPS_RES_COMMIT_LIMIT) return;
- die(res, "stress MVT");
-}
-
-
-int main(int argc, char *argv[])
-{
- mps_arena_t arena;
- mps_pool_debug_option_s *bothOptions;
-
- bothOptions = MPS_PF_ALIGN == 8 ? &bothOptions8 : &bothOptions16;
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
+ MPS_ARGS_BEGIN(args) {
+ mps_align_t align = (mps_align_t)1 << (rnd() % 6);
+ MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
+ die(stress(arena, NULL, align, randomSizeAligned, "MV",
+ mps_class_mv(), args), "stress MV");
+ } MPS_ARGS_END(args);
+
+ MPS_ARGS_BEGIN(args) {
+ mps_align_t align = (mps_align_t)1 << (rnd() % 6);
+ MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
+ MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, options);
+ die(stress(arena, options, align, randomSizeAligned, "MV debug",
+ mps_class_mv_debug(), args), "stress MV debug");
+ } MPS_ARGS_END(args);
+
+ MPS_ARGS_BEGIN(args) {
+ mps_align_t align = sizeof(void *) << (rnd() % 4);
+ MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
+ die(stress(arena, NULL, align, randomSizeAligned, "MVT",
+ mps_class_mvt(), args), "stress MVT");
+ } MPS_ARGS_END(args);
- die(mps_arena_create(&arena, mps_arena_class_vm(), 2*testArenaSIZE),
- "mps_arena_create");
- die(mps_arena_commit_limit_set(arena, testArenaSIZE), "commit limit");
- testInArena(arena, &fenceOptions);
mps_arena_destroy(arena);
+}
- die(mps_arena_create(&arena, mps_arena_class_vmnz(), 2*testArenaSIZE),
- "mps_arena_create");
- testInArena(arena, bothOptions);
- mps_arena_destroy(arena);
- die(mps_arena_create(&arena, mps_arena_class_cl(),
- testArenaSIZE, malloc(testArenaSIZE)),
- "mps_arena_create");
- testInArena(arena, bothOptions);
- mps_arena_destroy(arena);
+int main(int argc, char *argv[])
+{
+ testlib_init(argc, argv);
+
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 2 * testArenaSIZE);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(2*testArenaSIZE));
+ MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, testArenaSIZE);
+ test(mps_arena_class_vm(), args, &fenceOptions);
+ } MPS_ARGS_END(args);
+
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 2 * testArenaSIZE);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(2*testArenaSIZE));
+ test(mps_arena_class_vm(), args, &bothOptions);
+ } MPS_ARGS_END(args);
+
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_CL_BASE, malloc(testArenaSIZE));
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE));
+ test(mps_arena_class_cl(), args, &bothOptions);
+ } MPS_ARGS_END(args);
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
return 0;
@@ -217,7 +247,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/arena.c b/code/arena.c
index f2e94f99fe..f68acd16c1 100644
--- a/code/arena.c
+++ b/code/arena.c
@@ -1,33 +1,57 @@
/* arena.c: ARENA ALLOCATION FEATURES
*
* $Id$
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .sources: is the main design document. */
#include "tract.h"
#include "poolmv.h"
#include "mpm.h"
+#include "cbs.h"
+#include "bt.h"
+#include "poolmfs.h"
+#include "mpscmfs.h"
+
SRCID(arena, "$Id$");
-/* ArenaControlPool -- get the control pool */
+#define ArenaControlPool(arena) MVPool(&(arena)->controlPoolStruct)
+#define ArenaCBSBlockPool(arena) MFSPool(&(arena)->freeCBSBlockPoolStruct)
+#define ArenaFreeLand(arena) CBSLand(&(arena)->freeLandStruct)
+
+
+/* ArenaGrainSizeCheck -- check that size is a valid arena grain size */
+
+Bool ArenaGrainSizeCheck(Size size)
+{
+ CHECKL(size > 0);
+ /* */
+ CHECKL(SizeIsAligned(size, MPS_PF_ALIGN));
+ /* Grain size must be a power of 2 for the tract lookup and the
+ * zones to work. */
+ CHECKL(SizeIsP2(size));
-#define ArenaControlPool(arena) MV2Pool(&(arena)->controlPoolStruct)
+ return TRUE;
+}
/* Forward declarations */
static void ArenaTrivCompact(Arena arena, Trace trace);
+static void arenaFreePage(Arena arena, Addr base, Pool pool);
+static void arenaFreeLandFinish(Arena arena);
/* ArenaTrivDescribe -- produce trivial description of an arena */
-static Res ArenaTrivDescribe(Arena arena, mps_lib_FILE *stream)
+static Res ArenaTrivDescribe(Arena arena, mps_lib_FILE *stream, Count depth)
{
- if (!TESTT(Arena, arena)) return ResFAIL;
- if (stream == NULL) return ResFAIL;
+ if (!TESTT(Arena, arena))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
/* .describe.triv.never-called-from-subclass-method:
* This Triv method seems to assume that it will never get called
@@ -41,8 +65,8 @@ static Res ArenaTrivDescribe(Arena arena, mps_lib_FILE *stream)
* subclass describe method should avoid invoking
* ARENA_SUPERCLASS()->describe. RHSK 2007-04-27.
*/
- return WriteF(stream,
- " No class-specific description available.\n", NULL);
+ return WriteF(stream, depth,
+ " No class-specific description available.\n", NULL);
}
@@ -62,15 +86,15 @@ DEFINE_CLASS(AbstractArenaClass, class)
class->varargs = ArgTrivVarargs;
class->init = NULL;
class->finish = NULL;
- class->reserved = NULL;
class->purgeSpare = ArenaNoPurgeSpare;
class->extend = ArenaNoExtend;
- class->alloc = NULL;
+ class->grow = ArenaNoGrow;
class->free = NULL;
class->chunkInit = NULL;
class->chunkFinish = NULL;
class->compact = ArenaTrivCompact;
class->describe = ArenaTrivDescribe;
+ class->pagesMarkAllocated = NULL;
class->sig = ArenaClassSig;
}
@@ -79,7 +103,7 @@ DEFINE_CLASS(AbstractArenaClass, class)
Bool ArenaClassCheck(ArenaClass class)
{
- CHECKL(ProtocolClassCheck(&class->protocol));
+ CHECKD(ProtocolClass, &class->protocol);
CHECKL(class->name != NULL); /* Should be <=6 char C identifier */
CHECKL(class->size >= sizeof(ArenaStruct));
/* Offset of generic Pool within class-specific instance cannot be */
@@ -89,15 +113,15 @@ 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->alloc));
+ CHECKL(FUNCHECK(class->grow));
CHECKL(FUNCHECK(class->free));
CHECKL(FUNCHECK(class->chunkInit));
CHECKL(FUNCHECK(class->chunkFinish));
CHECKL(FUNCHECK(class->compact));
CHECKL(FUNCHECK(class->describe));
+ CHECKL(FUNCHECK(class->pagesMarkAllocated));
CHECKS(ArenaClass, class);
return TRUE;
}
@@ -116,18 +140,21 @@ Bool ArenaCheck(Arena arena)
CHECKD(MV, &arena->controlPoolStruct);
CHECKD(Reservoir, &arena->reservoirStruct);
}
- /* Can't check that limit>=size because we may call ArenaCheck */
- /* while the size is being adjusted. */
+ /* .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);
CHECKL(ShiftCheck(arena->zoneShift));
- CHECKL(AlignCheck(arena->alignment));
- /* Tract allocation must be platform-aligned. */
- CHECKL(arena->alignment >= MPS_PF_ALIGN);
- /* Stripes can't be smaller than pages. */
- CHECKL(((Size)1 << arena->zoneShift) >= arena->alignment);
+ CHECKL(ArenaGrainSizeCheck(arena->grainSize));
+
+ /* Stripes can't be smaller than grains. */
+ CHECKL(((Size)1 << arena->zoneShift) >= arena->grainSize);
if (arena->lastTract == NULL) {
CHECKL(arena->lastTractBase == (Addr)0);
@@ -138,51 +165,79 @@ Bool ArenaCheck(Arena arena)
if (arena->primary != NULL) {
CHECKD(Chunk, arena->primary);
}
- CHECKL(RingCheck(&arena->chunkRing));
+ CHECKD_NOSIG(Ring, &arena->chunkRing);
+ /* Can't use CHECKD_NOSIG because TreeEMPTY is NULL. */
+ CHECKL(TreeCheck(ArenaChunkTree(arena)));
+ /* TODO: check that the chunkRing and chunkTree have identical members */
/* nothing to check for chunkSerial */
- CHECKD(ChunkCacheEntry, &arena->chunkCache);
-
- CHECKL(LocusCheck(arena));
+ CHECKL(LocusCheck(arena));
+
+ CHECKL(BoolCheck(arena->hasFreeLand));
+ if (arena->hasFreeLand)
+ CHECKD(Land, ArenaFreeLand(arena));
+
+ CHECKL(BoolCheck(arena->zoned));
+
return TRUE;
}
/* ArenaInit -- initialize the generic part of the arena
*
- * .init.caller: Unlike PoolInit, this is called by the class init
- * methods, not the generic Create. This is because the class is
- * responsible for allocating the descriptor. */
+ * .init.caller: ArenaInit is called by class->init (which is called
+ * by ArenaCreate). The initialization must proceed in this order, as
+ * opposed to class->init being called by ArenaInit, which would
+ * correspond to the initialization order for pools and other objects,
+ * because the memory for the arena structure is not available until
+ * it has been allocated by the arena class.
+ */
-Res ArenaInit(Arena arena, ArenaClass class)
+Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args)
{
Res res;
+ Bool zoned = ARENA_DEFAULT_ZONED;
+ Size commitLimit = ARENA_DEFAULT_COMMIT_LIMIT;
+ Size spareCommitLimit = ARENA_DEFAULT_SPARE_COMMIT_LIMIT;
+ mps_arg_s arg;
- /* We do not check the arena argument, because it's _supposed_ to */
- /* point to an uninitialized block of memory. */
+ AVER(arena != NULL);
AVERT(ArenaClass, class);
+ AVERT(ArenaGrainSize, grainSize);
+
+ if (ArgPick(&arg, args, MPS_KEY_ARENA_ZONED))
+ zoned = arg.val.b;
+ if (ArgPick(&arg, args, MPS_KEY_COMMIT_LIMIT))
+ commitLimit = arg.val.size;
+ if (ArgPick(&arg, args, MPS_KEY_SPARE_COMMIT_LIMIT))
+ spareCommitLimit = arg.val.size;
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) */
- arena->commitLimit = (Size)-1;
+ arena->commitLimit = commitLimit;
arena->spareCommitted = (Size)0;
- arena->spareCommitLimit = ARENA_INIT_SPARE_COMMIT_LIMIT;
- /* alignment is usually overridden by init */
- arena->alignment = (Align)1 << ARENA_ZONESHIFT;
+ arena->spareCommitLimit = spareCommitLimit;
+ arena->grainSize = grainSize;
/* zoneShift is usually overridden by init */
arena->zoneShift = ARENA_ZONESHIFT;
arena->poolReady = FALSE; /* */
arena->lastTract = NULL;
arena->lastTractBase = NULL;
+ arena->hasFreeLand = FALSE;
+ arena->freeZones = ZoneSetUNIV;
+ arena->zoned = zoned;
arena->primary = NULL;
RingInit(&arena->chunkRing);
+ arena->chunkTree = TreeEMPTY;
arena->chunkSerial = (Serial)0;
- ChunkCacheEntryInit(&arena->chunkCache);
-
+ SplayTreeInit(ArenaSegSplay(arena),
+ SegCompare,
+ SegKey,
+ SplayTrivUpdate);
+
LocusInit(arena);
res = GlobalsInit(ArenaGlobals(arena));
@@ -190,6 +245,23 @@ Res ArenaInit(Arena arena, ArenaClass class)
goto failGlobalsInit;
arena->sig = ArenaSig;
+ AVERT(Arena, arena);
+
+ /* Initialise a pool to hold the CBS blocks for the arena's free
+ * land. This pool can't be allowed to extend itself using
+ * ArenaAlloc because it is used to implement ArenaAlloc, so
+ * MFSExtendSelf is set to FALSE. Failures to extend are handled
+ * where the free land is used: see arenaFreeLandInsertExtend. */
+
+ MPS_ARGS_BEGIN(piArgs) {
+ MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSZonedBlockStruct));
+ MPS_ARGS_ADD(piArgs, MPS_KEY_EXTEND_BY, ArenaGrainSize(arena));
+ MPS_ARGS_ADD(piArgs, MFSExtendSelf, FALSE);
+ res = PoolInit(ArenaCBSBlockPool(arena), arena, PoolClassMFS(), piArgs);
+ } MPS_ARGS_END(piArgs);
+ AVER(res == ResOK); /* no allocation, no failure expected */
+ if (res != ResOK)
+ goto failMFSInit;
/* initialize the reservoir, */
res = ReservoirInit(&arena->reservoirStruct, arena);
@@ -200,6 +272,8 @@ Res ArenaInit(Arena arena, ArenaClass class)
return ResOK;
failReservoirInit:
+ PoolFinish(ArenaCBSBlockPool(arena));
+failMFSInit:
GlobalsFinish(ArenaGlobals(arena));
failGlobalsInit:
return res;
@@ -214,12 +288,53 @@ Res ArenaInit(Arena arena, ArenaClass class)
* platforms, knowing that it has no effect. To do that, the key must
* exist on all platforms. */
-ARG_DEFINE_KEY(vmw3_top_down, Bool);
+ARG_DEFINE_KEY(VMW3_TOP_DOWN, Bool);
/* ArenaCreate -- create the arena and call initializers */
-ARG_DEFINE_KEY(arena_size, Size);
+ARG_DEFINE_KEY(ARENA_GRAIN_SIZE, Size);
+ARG_DEFINE_KEY(ARENA_SIZE, Size);
+ARG_DEFINE_KEY(ARENA_ZONED, Bool);
+ARG_DEFINE_KEY(COMMIT_LIMIT, Size);
+ARG_DEFINE_KEY(SPARE_COMMIT_LIMIT, Size);
+
+static Res arenaFreeLandInit(Arena arena)
+{
+ Res res;
+
+ AVERT(Arena, arena);
+ AVER(!arena->hasFreeLand);
+ AVER(arena->primary != NULL);
+
+ /* Initialise the free land. */
+ MPS_ARGS_BEGIN(liArgs) {
+ MPS_ARGS_ADD(liArgs, CBSBlockPool, ArenaCBSBlockPool(arena));
+ res = LandInit(ArenaFreeLand(arena), CBSZonedLandClassGet(), arena,
+ ArenaGrainSize(arena), arena, liArgs);
+ } MPS_ARGS_END(liArgs);
+ AVER(res == ResOK); /* no allocation, no failure expected */
+ if (res != ResOK)
+ goto failLandInit;
+
+ /* With the primary chunk initialised we can add page memory to the
+ * free land that describes the free address space in the primary
+ * chunk. */
+ res = ArenaFreeLandInsert(arena,
+ PageIndexBase(arena->primary,
+ arena->primary->allocBase),
+ arena->primary->limit);
+ if (res != ResOK)
+ goto failFreeLandInsert;
+
+ arena->hasFreeLand = TRUE;
+ return ResOK;
+
+failFreeLandInsert:
+ LandFinish(ArenaFreeLand(arena));
+failLandInit:
+ return res;
+}
Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args)
{
@@ -228,7 +343,7 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args)
AVER(arenaReturn != NULL);
AVERT(ArenaClass, class);
- AVER(ArgListCheck(args));
+ AVERT(ArgList, args);
/* We must initialise the event subsystem very early, because event logging
will start as soon as anything interesting happens and expect to write
@@ -240,12 +355,16 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args)
if (res != ResOK)
goto failInit;
- /* arena->alignment must have been set up by *class->init() */
- if (arena->alignment > ((Size)1 << arena->zoneShift)) {
+ /* Grain size must have been set up by *class->init() */
+ if (ArenaGrainSize(arena) > ((Size)1 << arena->zoneShift)) {
res = ResMEMORY; /* size was too small */
goto failStripeSize;
}
+ res = arenaFreeLandInit(arena);
+ if (res != ResOK)
+ goto failFreeLandInit;
+
res = ControlInit(arena);
if (res != ResOK)
goto failControlInit;
@@ -261,6 +380,8 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args)
failGlobalsCompleteCreate:
ControlFinish(arena);
failControlInit:
+ arenaFreeLandFinish(arena);
+failFreeLandInit:
failStripeSize:
(*class->finish)(arena);
failInit:
@@ -276,16 +397,51 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args)
void ArenaFinish(Arena arena)
{
+ PoolFinish(ArenaCBSBlockPool(arena));
ReservoirFinish(ArenaReservoir(arena));
arena->sig = SigInvalid;
GlobalsFinish(ArenaGlobals(arena));
LocusFinish(arena);
+ SplayTreeFinish(ArenaSegSplay(arena));
RingFinish(&arena->chunkRing);
+ AVER(ArenaChunkTree(arena) == TreeEMPTY);
}
/* ArenaDestroy -- destroy the arena */
+static void arenaMFSPageFreeVisitor(Pool pool, Addr base, Size size,
+ void *closureP, Size closureS)
+{
+ AVERT(Pool, pool);
+ AVER(closureP == UNUSED_POINTER);
+ AVER(closureS == UNUSED_SIZE);
+ UNUSED(closureP);
+ UNUSED(closureS);
+ UNUSED(size);
+ AVER(size == ArenaGrainSize(PoolArena(pool)));
+ arenaFreePage(PoolArena(pool), base, pool);
+}
+
+static void arenaFreeLandFinish(Arena arena)
+{
+ AVERT(Arena, arena);
+ AVER(arena->hasFreeLand);
+
+ /* We're about to free the memory occupied by the free land, which
+ contains a CBS. We want to make sure that LandFinish doesn't try
+ to check the CBS, so nuke it here. TODO: LandReset? */
+ arena->freeLandStruct.splayTreeStruct.root = TreeEMPTY;
+
+ /* The CBS block pool can't free its own memory via ArenaFree because
+ * that would use the free land. */
+ MFSFinishExtents(ArenaCBSBlockPool(arena), arenaMFSPageFreeVisitor,
+ UNUSED_POINTER, UNUSED_SIZE);
+
+ arena->hasFreeLand = FALSE;
+ LandFinish(ArenaFreeLand(arena));
+}
+
void ArenaDestroy(Arena arena)
{
AVERT(Arena, arena);
@@ -295,9 +451,12 @@ void ArenaDestroy(Arena arena)
/* Empty the reservoir - see */
ReservoirSetLimit(ArenaReservoir(arena), 0);
- arena->poolReady = FALSE;
ControlFinish(arena);
+ /* We must tear down the free land before the chunks, because pages
+ * containing CBS blocks might be allocated in those chunks. */
+ arenaFreeLandFinish(arena);
+
/* Call class-specific finishing. This will call ArenaFinish. */
(*arena->class->finish)(arena);
@@ -312,9 +471,10 @@ Res ControlInit(Arena arena)
Res res;
AVERT(Arena, arena);
+ AVER(!arena->poolReady);
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, CONTROL_EXTEND_BY);
- res = PoolInit(&arena->controlPoolStruct.poolStruct, arena,
+ res = PoolInit(MVPool(&arena->controlPoolStruct), arena,
PoolClassMV(), args);
} MPS_ARGS_END(args);
if (res != ResOK)
@@ -329,73 +489,78 @@ Res ControlInit(Arena arena)
void ControlFinish(Arena arena)
{
AVERT(Arena, arena);
+ AVER(arena->poolReady);
arena->poolReady = FALSE;
- PoolFinish(&arena->controlPoolStruct.poolStruct);
+ PoolFinish(MVPool(&arena->controlPoolStruct));
}
/* ArenaDescribe -- describe the arena */
-Res ArenaDescribe(Arena arena, mps_lib_FILE *stream)
+Res ArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth)
{
Res res;
- Size reserved;
- if (!TESTT(Arena, arena)) return ResFAIL;
- if (stream == NULL) return ResFAIL;
+ if (!TESTT(Arena, arena))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
- res = WriteF(stream, "Arena $P {\n", (WriteFP)arena,
+ res = WriteF(stream, depth, "Arena $P {\n", (WriteFP)arena,
" class $P (\"$S\")\n",
- (WriteFP)arena->class, arena->class->name,
+ (WriteFP)arena->class, (WriteFS)arena->class->name,
NULL);
- if (res != ResOK) return res;
+ if (res != ResOK)
+ return res;
if (arena->poolReady) {
- res = WriteF(stream,
- " controlPool $P\n", (WriteFP)&arena->controlPoolStruct,
+ res = WriteF(stream, depth + 2,
+ "controlPool $P\n", (WriteFP)&arena->controlPoolStruct,
NULL);
- if (res != ResOK) return res;
+ if (res != ResOK)
+ return res;
}
- /* Note: this Describe clause calls a function */
- reserved = ArenaReserved(arena);
- res = WriteF(stream,
- " reserved $W <-- "
- "total size of address-space reserved\n",
- (WriteFW)reserved,
- NULL);
- if (res != ResOK) return res;
-
- res = WriteF(stream,
- " committed $W <-- "
- "total bytes currently stored (in RAM or swap)\n",
- (WriteFW)arena->committed,
- " commitLimit $W\n", (WriteFW)arena->commitLimit,
- " spareCommitted $W\n", (WriteFW)arena->spareCommitted,
- " spareCommitLimit $W\n", (WriteFW)arena->spareCommitLimit,
- " zoneShift $U\n", (WriteFU)arena->zoneShift,
- " alignment $W\n", (WriteFW)arena->alignment,
+ res = WriteF(stream, depth + 2,
+ "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,
+ "zoneShift $U\n", (WriteFU)arena->zoneShift,
+ "grainSize $W\n", (WriteFW)arena->grainSize,
+ "lastTract $P\n", (WriteFP)arena->lastTract,
+ "lastTractBase $P\n", (WriteFP)arena->lastTractBase,
+ "primary $P\n", (WriteFP)arena->primary,
+ "hasFreeLand $S\n", WriteFYesNo(arena->hasFreeLand),
+ "freeZones $B\n", (WriteFB)arena->freeZones,
+ "zoned $S\n", WriteFYesNo(arena->zoned),
NULL);
- if (res != ResOK) return res;
+ if (res != ResOK)
+ return res;
- res = WriteF(stream,
- " droppedMessages $U$S\n", (WriteFU)arena->droppedMessages,
+ res = WriteF(stream, depth + 2,
+ "droppedMessages $U$S\n", (WriteFU)arena->droppedMessages,
(arena->droppedMessages == 0 ? "" : " -- MESSAGES DROPPED!"),
NULL);
- if (res != ResOK) return res;
+ if (res != ResOK)
+ return res;
- res = (*arena->class->describe)(arena, stream);
- if (res != ResOK) return res;
+ res = (*arena->class->describe)(arena, stream, depth);
+ if (res != ResOK)
+ return res;
- /* Do not call GlobalsDescribe: it makes too much output, thanks.
- * RHSK 2007-04-27
- */
-#if 0
- res = GlobalsDescribe(ArenaGlobals(arena), stream);
- if (res != ResOK) return res;
-#endif
+ res = WriteF(stream, depth + 2, "Globals {\n", NULL);
+ if (res != ResOK)
+ return res;
+ res = GlobalsDescribe(ArenaGlobals(arena), stream, depth + 4);
+ if (res != ResOK)
+ return res;
+ res = WriteF(stream, depth + 2, "} Globals\n", NULL);
+ if (res != ResOK)
+ return res;
- res = WriteF(stream,
+ res = WriteF(stream, depth,
"} Arena $P ($U)\n", (WriteFP)arena,
(WriteFU)arena->serial,
NULL);
@@ -403,47 +568,76 @@ Res ArenaDescribe(Arena arena, mps_lib_FILE *stream)
}
-/* ArenaDescribeTracts -- describe all the tracts in the arena */
+/* arenaDescribeTractsInChunk -- describe the tracts in a chunk */
-Res ArenaDescribeTracts(Arena arena, mps_lib_FILE *stream)
+static Res arenaDescribeTractsInChunk(Chunk chunk, mps_lib_FILE *stream, Count depth)
{
Res res;
- Tract tract;
- Bool b;
- Addr oldLimit, base, limit;
- Size size;
+ Index pi;
+
+ if (!TESTT(Chunk, chunk))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
+
+ res = WriteF(stream, depth, "Chunk [$P, $P) ($U) {\n",
+ (WriteFP)chunk->base, (WriteFP)chunk->limit,
+ (WriteFU)chunk->serial,
+ NULL);
+ if (res != ResOK)
+ return res;
- if (!TESTT(Arena, arena)) return ResFAIL;
- if (stream == NULL) return ResFAIL;
-
- b = TractFirst(&tract, arena);
- oldLimit = TractBase(tract);
- while (b) {
- base = TractBase(tract);
- limit = TractLimit(tract);
- size = ArenaAlign(arena);
-
- if (TractBase(tract) > oldLimit) {
- res = WriteF(stream,
- "[$P, $P) $W $U ---\n",
- (WriteFP)oldLimit, (WriteFP)base,
- (WriteFW)AddrOffset(oldLimit, base),
- (WriteFU)AddrOffset(oldLimit, base),
+ for (pi = chunk->allocBase; pi < chunk->pages; ++pi) {
+ if (BTGet(chunk->allocTable, pi)) {
+ Tract tract = PageTract(ChunkPage(chunk, pi));
+ res = WriteF(stream, depth + 2, "[$P, $P)",
+ (WriteFP)TractBase(tract),
+ (WriteFP)TractLimit(tract, ChunkArena(chunk)),
NULL);
- if (res != ResOK) return res;
+ if (res != ResOK)
+ return res;
+ if (TractHasPool(tract)) {
+ Pool pool = TractPool(tract);
+ res = WriteF(stream, 0, " $P $U ($S)",
+ (WriteFP)pool,
+ (WriteFU)(pool->serial),
+ (WriteFS)(pool->class->name),
+ NULL);
+ if (res != ResOK)
+ return res;
+ }
+ res = WriteF(stream, 0, "\n", NULL);
+ if (res != ResOK)
+ return res;
}
+ }
- res = WriteF(stream,
- "[$P, $P) $W $U $P ($S)\n",
- (WriteFP)base, (WriteFP)limit,
- (WriteFW)size, (WriteFW)size,
- (WriteFP)TractPool(tract),
- (WriteFS)(TractPool(tract)->class->name),
- NULL);
- if (res != ResOK) return res;
- b = TractNext(&tract, arena, TractBase(tract));
- oldLimit = limit;
+ res = WriteF(stream, depth, "} Chunk [$P, $P)\n",
+ (WriteFP)chunk->base, (WriteFP)chunk->limit,
+ NULL);
+ return res;
+}
+
+
+/* ArenaDescribeTracts -- describe all the tracts in the arena */
+
+Res ArenaDescribeTracts(Arena arena, mps_lib_FILE *stream, Count depth)
+{
+ Ring node, next;
+ Res res;
+
+ if (!TESTT(Arena, arena))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
+
+ RING_FOR(node, &arena->chunkRing, next) {
+ Chunk chunk = RING_ELT(Chunk, arenaRing, node);
+ res = arenaDescribeTractsInChunk(chunk, stream, depth);
+ if (res != ResOK)
+ return res;
}
+
return ResOK;
}
@@ -466,7 +660,7 @@ Res ControlAlloc(void **baseReturn, Arena arena, size_t size,
AVERT(Arena, arena);
AVER(baseReturn != NULL);
AVER(size > 0);
- AVER(BoolCheck(withReservoirPermit));
+ AVERT(Bool, withReservoirPermit);
AVER(arena->poolReady);
res = PoolAlloc(&base, ArenaControlPool(arena), (Size)size,
@@ -494,69 +688,467 @@ void ControlFree(Arena arena, void* base, size_t size)
/* ControlDescribe -- describe the arena's control pool */
-Res ControlDescribe(Arena arena, mps_lib_FILE *stream)
+Res ControlDescribe(Arena arena, mps_lib_FILE *stream, Count depth)
+{
+ Res res;
+
+ if (!TESTT(Arena, arena))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
+
+ res = PoolDescribe(ArenaControlPool(arena), stream, depth);
+
+ return res;
+}
+
+
+/* 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;
+
+ AVERT(Arena, arena);
+ AVERT(Chunk, chunk);
+ tree = &chunk->chunkTree;
+
+ inserted = TreeInsert(&updatedTree, ArenaChunkTree(arena),
+ tree, ChunkKey(tree), ChunkCompare);
+ AVER(inserted);
+ AVER(updatedTree);
+ TreeBalance(&updatedTree);
+ arena->chunkTree = updatedTree;
+ RingAppend(&arena->chunkRing, &chunk->arenaRing);
+
+ arena->reserved += ChunkReserved(chunk);
+
+ /* As part of the bootstrap, the first created chunk becomes the primary
+ chunk. This step allows ArenaFreeLandInsert to allocate pages. */
+ if (arena->primary == NULL)
+ arena->primary = 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);
+
+ 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;
+ }
+}
+
+
+/* arenaAllocPage -- allocate one page from the arena
+ *
+ * This is a primitive allocator used to allocate pages for the arena
+ * Land. It is called rarely and can use a simple search. It may not
+ * use the Land or any pool, because it is used as part of the
+ * bootstrap. See design.mps.bootstrap.land.sol.alloc.
+ */
+
+static Res arenaAllocPageInChunk(Addr *baseReturn, Chunk chunk, Pool pool)
{
Res res;
+ Index basePageIndex, limitPageIndex;
+ Arena arena;
+
+ AVER(baseReturn != NULL);
+ AVERT(Chunk, chunk);
+ AVERT(Pool, pool);
+ arena = ChunkArena(chunk);
+
+ if (!BTFindShortResRange(&basePageIndex, &limitPageIndex,
+ chunk->allocTable,
+ chunk->allocBase, chunk->pages, 1))
+ return ResRESOURCE;
+
+ res = (*arena->class->pagesMarkAllocated)(arena, chunk,
+ basePageIndex, 1,
+ pool);
+ if (res != ResOK)
+ return res;
- if (!TESTT(Arena, arena)) return ResFAIL;
- if (stream == NULL) return ResFAIL;
+ *baseReturn = PageIndexBase(chunk, basePageIndex);
+ return ResOK;
+}
- res = PoolDescribe(ArenaControlPool(arena), stream);
+static Res arenaAllocPage(Addr *baseReturn, Arena arena, Pool pool)
+{
+ Res res;
+
+ AVER(baseReturn != NULL);
+ AVERT(Arena, arena);
+ AVERT(Pool, pool);
+ /* Favour the primary chunk, because pages allocated this way aren't
+ currently freed, and we don't want to prevent chunks being destroyed. */
+ /* TODO: Consider how the ArenaCBSBlockPool might free pages. */
+ res = arenaAllocPageInChunk(baseReturn, arena->primary, pool);
+ if (res != ResOK) {
+ Ring node, next;
+ RING_FOR(node, &arena->chunkRing, next) {
+ Chunk chunk = RING_ELT(Chunk, arenaRing, node);
+ if (chunk != arena->primary) {
+ res = arenaAllocPageInChunk(baseReturn, chunk, pool);
+ if (res == ResOK)
+ break;
+ }
+ }
+ }
return res;
}
+/* arenaFreePage -- free page allocated by arenaAllocPage */
+
+static void arenaFreePage(Arena arena, Addr base, Pool pool)
+{
+ AVERT(Arena, arena);
+ AVERT(Pool, pool);
+ (*arena->class->free)(base, ArenaGrainSize(arena), pool);
+}
+
+
+/* arenaExtendCBSBlockPool -- add a page of memory to the CBS block pool
+ *
+ * IMPORTANT: Must be followed by arenaExcludePage to ensure that the
+ * page doesn't get allocated by ArenaAlloc. See .insert.exclude.
+ */
+
+static Res arenaExtendCBSBlockPool(Range pageRangeReturn, Arena arena)
+{
+ Addr pageBase;
+ Res res;
+
+ res = arenaAllocPage(&pageBase, arena, ArenaCBSBlockPool(arena));
+ if (res != ResOK)
+ return res;
+ MFSExtend(ArenaCBSBlockPool(arena), pageBase, ArenaGrainSize(arena));
+
+ RangeInitSize(pageRangeReturn, pageBase, ArenaGrainSize(arena));
+ return ResOK;
+}
+
+/* arenaExcludePage -- exclude CBS block pool's page from free land
+ *
+ * Exclude the page we specially allocated for the CBS block pool
+ * so that it doesn't get reallocated.
+ */
+
+static void arenaExcludePage(Arena arena, Range pageRange)
+{
+ RangeStruct oldRange;
+ Res res;
+
+ res = LandDelete(&oldRange, ArenaFreeLand(arena), pageRange);
+ AVER(res == ResOK); /* we just gave memory to the Land */
+}
+
+
+/* arenaFreeLandInsertExtend -- add range to arena's free land, maybe
+ * extending block pool
+ *
+ * The arena's free land can't get memory for its block pool in the
+ * usual way (via ArenaAlloc), because it is the mechanism behind
+ * ArenaAlloc! So we extend the block pool via a back door (see
+ * arenaExtendCBSBlockPool). See design.mps.bootstrap.land.sol.pool.
+ *
+ * Only fails if it can't get a page for the block pool.
+ */
+
+static Res arenaFreeLandInsertExtend(Range rangeReturn, Arena arena,
+ Range range)
+{
+ Res res;
+
+ AVER(rangeReturn != NULL);
+ AVERT(Arena, arena);
+ AVERT(Range, range);
+
+ res = LandInsert(rangeReturn, ArenaFreeLand(arena), range);
+
+ if (res == ResLIMIT) { /* CBS block pool ran out of blocks */
+ RangeStruct pageRange;
+ res = arenaExtendCBSBlockPool(&pageRange, arena);
+ if (res != ResOK)
+ return res;
+ /* .insert.exclude: Must insert before exclude so that we can
+ bootstrap when the zoned CBS is empty. */
+ res = LandInsert(rangeReturn, ArenaFreeLand(arena), range);
+ AVER(res == ResOK); /* we just gave memory to the CBS block pool */
+ arenaExcludePage(arena, &pageRange);
+ }
+
+ return ResOK;
+}
+
+
+/* arenaFreeLandInsertSteal -- add range to arena's free land, maybe
+ * stealing memory
+ *
+ * See arenaFreeLandInsertExtend. This function may only be applied to
+ * mapped pages and may steal them to store Land nodes if it's unable
+ * to allocate space for CBS blocks.
+ *
+ * IMPORTANT: May update rangeIO.
+ */
+
+static void arenaFreeLandInsertSteal(Range rangeReturn, Arena arena,
+ Range rangeIO)
+{
+ Res res;
+
+ AVER(rangeReturn != NULL);
+ AVERT(Arena, arena);
+ AVERT(Range, rangeIO);
+
+ res = arenaFreeLandInsertExtend(rangeReturn, arena, rangeIO);
+
+ if (res != ResOK) {
+ Addr pageBase;
+ Tract tract;
+ AVER(ResIsAllocFailure(res));
+
+ /* Steal a page from the memory we're about to free. */
+ AVER(RangeSize(rangeIO) >= ArenaGrainSize(arena));
+ pageBase = RangeBase(rangeIO);
+ RangeInit(rangeIO, AddrAdd(pageBase, ArenaGrainSize(arena)),
+ RangeLimit(rangeIO));
+
+ /* Steal the tract from its owning pool. */
+ tract = TractOfBaseAddr(arena, pageBase);
+ TractFinish(tract);
+ TractInit(tract, ArenaCBSBlockPool(arena), pageBase);
+
+ MFSExtend(ArenaCBSBlockPool(arena), pageBase, ArenaGrainSize(arena));
+
+ /* Try again. */
+ res = LandInsert(rangeReturn, ArenaFreeLand(arena), rangeIO);
+ AVER(res == ResOK); /* we just gave memory to the CBS block pool */
+ }
+
+ AVER(res == ResOK); /* not expecting other kinds of error from the Land */
+}
+
+
+/* ArenaFreeLandInsert -- add range to arena's free land, maybe extending
+ * block pool
+ *
+ * The inserted block of address space may not abut any existing block.
+ * This restriction ensures that we don't coalesce chunks and allocate
+ * object across the boundary, preventing chunk deletion.
+ */
+
+Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit)
+{
+ RangeStruct range, oldRange;
+ Res res;
+
+ AVERT(Arena, arena);
+
+ RangeInit(&range, base, limit);
+ res = arenaFreeLandInsertExtend(&oldRange, arena, &range);
+ if (res != ResOK)
+ return res;
+
+ /* .chunk.no-coalesce: Make sure it didn't coalesce. We don't want
+ chunks to coalesce so that there are no chunk-crossing
+ allocations that would prevent chunks being destroyed. See
+ for the mechanism that ensures that
+ chunks never coalesce. */
+ AVER(RangesEqual(&oldRange, &range));
+
+ return ResOK;
+}
+
+
+/* ArenaFreeLandDelete -- remove range from arena's free land, maybe
+ * extending block pool
+ *
+ * This is called from ChunkFinish in order to remove address space from
+ * the arena.
+ *
+ * IMPORTANT: May only be called on whole chunk ranges, because we don't
+ * deal with the case where the range is coalesced. This restriction would
+ * be easy to lift by extending the block pool on error, but doesn't happen,
+ * so we can't test that path.
+ */
+
+void ArenaFreeLandDelete(Arena arena, Addr base, Addr limit)
+{
+ RangeStruct range, oldRange;
+ Res res;
+
+ RangeInit(&range, base, limit);
+ res = LandDelete(&oldRange, ArenaFreeLand(arena), &range);
+
+ /* Shouldn't be any other kind of failure because we were only deleting
+ a non-coalesced block. See .chunk.no-coalesce and
+ . */
+ AVER(res == ResOK);
+}
+
+
+/* ArenaFreeLandAlloc -- allocate a continguous range of tracts of
+ * size bytes from the arena's free land.
+ *
+ * size, zones, and high are as for LandFindInZones.
+ *
+ * If successful, mark the allocated tracts as belonging to pool, set
+ * *tractReturn to point to the first tract in the range, and return
+ * ResOK.
+ */
+
+Res ArenaFreeLandAlloc(Tract *tractReturn, Arena arena, ZoneSet zones,
+ Bool high, Size size, Pool pool)
+{
+ RangeStruct range, oldRange;
+ Chunk chunk = NULL; /* suppress uninit warning */
+ Bool found, b;
+ Index baseIndex;
+ Count pages;
+ Res res;
+
+ AVER(tractReturn != NULL);
+ AVERT(Arena, arena);
+ /* ZoneSet is arbitrary */
+ AVER(size > (Size)0);
+ AVERT(Pool, pool);
+ AVER(arena == PoolArena(pool));
+ AVER(SizeIsArenaGrains(size, arena));
+
+ if (!arena->zoned)
+ zones = ZoneSetUNIV;
+
+ /* Step 1. Find a range of address space. */
+
+ res = LandFindInZones(&found, &range, &oldRange, ArenaFreeLand(arena),
+ size, zones, high);
+
+ if (res == ResLIMIT) { /* found block, but couldn't store info */
+ RangeStruct pageRange;
+ res = arenaExtendCBSBlockPool(&pageRange, arena);
+ if (res != ResOK) /* disastrously short on memory */
+ return res;
+ arenaExcludePage(arena, &pageRange);
+ res = LandFindInZones(&found, &range, &oldRange, ArenaFreeLand(arena),
+ size, zones, high);
+ AVER(res != ResLIMIT);
+ }
+
+ AVER(res == ResOK); /* unexpected error from ZoneCBS */
+ if (res != ResOK) /* defensive return */
+ return res;
+
+ if (!found) /* out of address space */
+ return ResRESOURCE;
+
+ /* Step 2. Make memory available in the address space range. */
+
+ b = ChunkOfAddr(&chunk, arena, RangeBase(&range));
+ AVER(b);
+ AVER(RangeIsAligned(&range, ChunkPageSize(chunk)));
+ baseIndex = INDEX_OF_ADDR(chunk, RangeBase(&range));
+ pages = ChunkSizeToPages(chunk, RangeSize(&range));
+
+ res = (*arena->class->pagesMarkAllocated)(arena, chunk, baseIndex, pages, pool);
+ if (res != ResOK)
+ goto failMark;
+
+ arena->freeZones = ZoneSetDiff(arena->freeZones,
+ ZoneSetOfRange(arena,
+ RangeBase(&range),
+ RangeLimit(&range)));
+
+ *tractReturn = PageTract(ChunkPage(chunk, baseIndex));
+ return ResOK;
+
+failMark:
+ {
+ Res insertRes = arenaFreeLandInsertExtend(&oldRange, arena, &range);
+ AVER(insertRes == ResOK); /* We only just deleted it. */
+ /* If the insert does fail, we lose some address space permanently. */
+ }
+ return res;
+}
+
+
/* ArenaAlloc -- allocate some tracts from the arena */
-Res ArenaAlloc(Addr *baseReturn, SegPref pref, Size size, Pool pool,
+Res ArenaAlloc(Addr *baseReturn, LocusPref pref, Size size, Pool pool,
Bool withReservoirPermit)
{
Res res;
Arena arena;
Addr base;
- Tract baseTract;
+ Tract tract;
Reservoir reservoir;
AVER(baseReturn != NULL);
- AVERT(SegPref, pref);
+ AVERT(LocusPref, pref);
AVER(size > (Size)0);
AVERT(Pool, pool);
- AVER(BoolCheck(withReservoirPermit));
+ AVERT(Bool, withReservoirPermit);
arena = PoolArena(pool);
AVERT(Arena, arena);
- AVER(SizeIsAligned(size, arena->alignment));
+ AVER(SizeIsArenaGrains(size, arena));
reservoir = ArenaReservoir(arena);
AVERT(Reservoir, reservoir);
- res = ReservoirEnsureFull(reservoir);
- if (res != ResOK) {
- AVER(ResIsAllocFailure(res));
- if (!withReservoirPermit)
- return res;
+ if (pool != ReservoirPool(reservoir)) {
+ res = ReservoirEnsureFull(reservoir);
+ if (res != ResOK) {
+ AVER(ResIsAllocFailure(res));
+ if (!withReservoirPermit)
+ return res;
+ }
}
- res = (*arena->class->alloc)(&base, &baseTract, pref, size, pool);
- if (res == ResOK) {
- goto goodAlloc;
- } else if (withReservoirPermit) {
- AVER(ResIsAllocFailure(res));
- res = ReservoirWithdraw(&base, &baseTract, reservoir, size, pool);
- if (res == ResOK)
- goto goodAlloc;
+ res = PolicyAlloc(&tract, arena, pref, size, pool);
+ if (res != ResOK) {
+ if (withReservoirPermit) {
+ Res resRes = ReservoirWithdraw(&base, &tract, reservoir, size, pool);
+ if (resRes != ResOK)
+ goto allocFail;
+ } else
+ goto allocFail;
}
- EVENT3(ArenaAllocFail, arena, size, pool);
- return res;
+
+ base = TractBase(tract);
-goodAlloc:
/* cache the tract - */
- arena->lastTract = baseTract;
+ arena->lastTract = tract;
arena->lastTractBase = base;
- EVENT5(ArenaAlloc, arena, baseTract, base, size, pool);
+ EVENT5(ArenaAlloc, arena, tract, base, size, pool);
+
*baseReturn = base;
return ResOK;
+
+allocFail:
+ EVENT3(ArenaAllocFail, arena, size, pool); /* TODO: Should have res? */
+ return res;
}
@@ -568,6 +1160,9 @@ void ArenaFree(Addr base, Size size, Pool pool)
Addr limit;
Reservoir reservoir;
Res res;
+ Addr wholeBase;
+ Size wholeSize;
+ RangeStruct range, oldRange;
AVERT(Pool, pool);
AVER(base != NULL);
@@ -576,8 +1171,8 @@ void ArenaFree(Addr base, Size size, Pool pool)
AVERT(Arena, arena);
reservoir = ArenaReservoir(arena);
AVERT(Reservoir, reservoir);
- AVER(AddrIsAligned(base, arena->alignment));
- AVER(SizeIsAligned(size, arena->alignment));
+ AVER(AddrIsArenaGrain(base, arena));
+ AVER(SizeIsArenaGrains(size, arena));
/* uncache the tract if in range - */
limit = AddrAdd(base, size);
@@ -585,19 +1180,33 @@ void ArenaFree(Addr base, Size size, Pool pool)
arena->lastTract = NULL;
arena->lastTractBase = (Addr)0;
}
-
- res = ReservoirEnsureFull(reservoir);
- if (res == ResOK) {
- (*arena->class->free)(base, size, pool);
- } else {
- AVER(ResIsAllocFailure(res));
- ReservoirDeposit(reservoir, base, size);
+
+ wholeBase = base;
+ wholeSize = size;
+
+ if (pool != ReservoirPool(reservoir)) {
+ res = ReservoirEnsureFull(reservoir);
+ if (res != ResOK) {
+ AVER(ResIsAllocFailure(res));
+ if (!ReservoirDeposit(reservoir, &base, &size))
+ goto allDeposited;
+ }
}
+ /* Just in case the shenanigans with the reservoir mucked this up. */
+ AVER(limit == AddrAdd(base, size));
+
+ RangeInit(&range, base, limit);
+
+ arenaFreeLandInsertSteal(&oldRange, arena, &range); /* may update range */
+
+ (*arena->class->free)(RangeBase(&range), RangeSize(&range), pool);
+
/* Freeing memory might create spare pages, but not more than this. */
CHECKL(arena->spareCommitted <= arena->spareCommitLimit);
- EVENT3(ArenaFree, arena, base, size);
+allDeposited:
+ EVENT3(ArenaFree, arena, wholeBase, wholeSize);
return;
}
@@ -605,7 +1214,7 @@ void ArenaFree(Addr base, Size size, Pool pool)
Size ArenaReserved(Arena arena)
{
AVERT(Arena, arena);
- return (*arena->class->reserved)(arena);
+ return arena->reserved;
}
Size ArenaCommitted(Arena arena)
@@ -638,7 +1247,6 @@ void ArenaSetSpareCommitLimit(Arena arena, Size limit)
}
EVENT2(SpareCommitLimitSet, arena, limit);
- return;
}
/* Used by arenas which don't use spare committed memory */
@@ -650,6 +1258,15 @@ Size ArenaNoPurgeSpare(Arena arena, Size size)
}
+Res ArenaNoGrow(Arena arena, LocusPref pref, Size size)
+{
+ AVERT(Arena, arena);
+ AVERT(LocusPref, pref);
+ UNUSED(size);
+ return ResRESOURCE;
+}
+
+
Size ArenaCommitLimit(Arena arena)
{
AVERT(Arena, arena);
@@ -706,6 +1323,15 @@ Size ArenaAvail(Arena arena)
}
+/* ArenaCollectable -- return estimate of collectable memory in arena */
+
+Size ArenaCollectable(Arena arena)
+{
+ /* Conservative estimate -- see job003929. */
+ return ArenaCommitted(arena) - ArenaSpareCommitted(arena);
+}
+
+
/* ArenaExtend -- Add a new chunk in the arena */
Res ArenaExtend(Arena arena, Addr base, Size size)
@@ -759,10 +1385,10 @@ static void ArenaTrivCompact(Arena arena, Trace trace)
Bool ArenaHasAddr(Arena arena, Addr addr)
{
- Seg seg;
+ Tract tract;
AVERT(Arena, arena);
- return SegOfAddr(&seg, arena, addr);
+ return TractOfAddr(&tract, arena, addr);
}
@@ -788,7 +1414,7 @@ Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr)
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/arenacl.c b/code/arenacl.c
index 473ac37a40..cd5c24fe46 100644
--- a/code/arenacl.c
+++ b/code/arenacl.c
@@ -1,7 +1,7 @@
/* arenacl.c: ARENA CLASS USING CLIENT MEMORY
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .design: See .
*
@@ -61,13 +61,14 @@ typedef struct ClientChunkStruct {
/* ClientChunkCheck -- check the consistency of a client chunk */
+ATTRIBUTE_UNUSED
static Bool ClientChunkCheck(ClientChunk clChunk)
{
Chunk chunk;
CHECKS(ClientChunk, clChunk);
chunk = ClientChunk2Chunk(clChunk);
- CHECKL(ChunkCheck(chunk));
+ CHECKD(Chunk, chunk);
CHECKL(clChunk->freePages <= chunk->pages);
/* check they don't overlap (knowing the order) */
CHECKL((Addr)(chunk + 1) < (Addr)chunk->allocTable);
@@ -77,19 +78,28 @@ static Bool ClientChunkCheck(ClientChunk clChunk)
/* ClientArenaCheck -- check the consistency of a client arena */
+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;
}
/* clientChunkCreate -- create a ClientChunk */
-static Res clientChunkCreate(Chunk *chunkReturn, Addr base, Addr limit,
- ClientArena clientArena)
+static Res clientChunkCreate(Chunk *chunkReturn, ClientArena clientArena,
+ Addr base, Addr limit)
{
+ Arena arena;
ClientChunk clChunk;
Chunk chunk;
Addr alignedBase;
@@ -99,14 +109,15 @@ static Res clientChunkCreate(Chunk *chunkReturn, Addr base, Addr limit,
void *p;
AVER(chunkReturn != NULL);
+ AVERT(ClientArena, clientArena);
+ arena = ClientArena2Arena(clientArena);
AVER(base != (Addr)0);
- /* TODO: Should refuse on small chunks, instead of AVERring. */
AVER(limit != (Addr)0);
AVER(limit > base);
/* Initialize boot block. */
/* Chunk has to be page-aligned, and the boot allocs must be within it. */
- alignedBase = AddrAlignUp(base, ARENA_CLIENT_PAGE_SIZE);
+ alignedBase = AddrAlignUp(base, ArenaGrainSize(arena));
AVER(alignedBase < limit);
res = BootBlockInit(boot, (void *)alignedBase, (void *)limit);
if (res != ResOK)
@@ -117,16 +128,17 @@ static Res clientChunkCreate(Chunk *chunkReturn, Addr base, Addr limit,
res = BootAlloc(&p, boot, sizeof(ClientChunkStruct), MPS_PF_ALIGN);
if (res != ResOK)
goto failChunkAlloc;
- clChunk = p; chunk = ClientChunk2Chunk(clChunk);
+ clChunk = p;
+ chunk = ClientChunk2Chunk(clChunk);
- res = ChunkInit(chunk, ClientArena2Arena(clientArena),
- alignedBase, AddrAlignDown(limit, ARENA_CLIENT_PAGE_SIZE),
- ARENA_CLIENT_PAGE_SIZE, boot);
+ res = ChunkInit(chunk, arena, alignedBase,
+ AddrAlignDown(limit, ArenaGrainSize(arena)),
+ AddrOffset(base, limit), boot);
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;
@@ -152,7 +164,6 @@ static Res ClientChunkInit(Chunk chunk, BootBlock boot)
/* chunk is supposed to be uninitialized, so don't check it. */
clChunk = Chunk2ClientChunk(chunk);
AVERT(BootBlock, boot);
- UNUSED(boot);
/* TODO: An old comment claimed this is too large.
Does it fail to exclude the page table or something? */
@@ -171,15 +182,35 @@ static Res ClientChunkInit(Chunk chunk, BootBlock boot)
/* clientChunkDestroy -- destroy a ClientChunk */
-static void clientChunkDestroy(Chunk chunk)
+static Bool clientChunkDestroy(Tree tree, void *closureP, Size closureS)
{
+ Arena arena;
+ Chunk chunk;
ClientChunk clChunk;
+ Size size;
+ AVERT(Tree, tree);
+ AVER(closureP == UNUSED_POINTER);
+ UNUSED(closureP);
+ AVER(closureS == UNUSED_SIZE);
+ UNUSED(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);
+
+ return TRUE;
}
@@ -188,7 +219,7 @@ static void clientChunkDestroy(Chunk chunk)
static void ClientChunkFinish(Chunk chunk)
{
/* Can't check chunk as it's not valid anymore. */
- UNUSED(chunk); NOOP;
+ UNUSED(chunk);
}
@@ -201,7 +232,7 @@ static void ClientArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs)
args[1].key = MPS_KEY_ARENA_CL_BASE;
args[1].val.addr = va_arg(varargs, Addr);
args[2].key = MPS_KEY_ARGS_END;
- AVER(ArgListCheck(args));
+ AVERT(ArgList, args);
}
@@ -213,7 +244,7 @@ static void ClientArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs)
* to do the generic part of init.
*/
-ARG_DEFINE_KEY(arena_cl_addr, Addr);
+ARG_DEFINE_KEY(ARENA_CL_BASE, Addr);
static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args)
{
@@ -222,20 +253,30 @@ static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args)
Size size;
Size clArenaSize; /* aligned size of ClientArenaStruct */
Addr base, limit, chunkBase;
+ Align grainSize = 1;
Res res;
Chunk chunk;
mps_arg_s arg;
AVER(arenaReturn != NULL);
AVER((ArenaClass)mps_arena_class_cl() == class);
- AVER(ArgListCheck(args));
+ AVERT(ArgList, args);
ArgRequire(&arg, args, MPS_KEY_ARENA_SIZE);
size = arg.val.size;
ArgRequire(&arg, args, MPS_KEY_ARENA_CL_BASE);
base = arg.val.addr;
+ if (ArgPick(&arg, args, MPS_KEY_ARENA_GRAIN_SIZE))
+ grainSize = arg.val.size;
+ grainSize = SizeAlignUp(grainSize, ARENA_CLIENT_GRAIN_SIZE);
+ grainSize = SizeAlignUp(grainSize, ProtGranularity());
AVER(base != (Addr)0);
+ AVERT(ArenaGrainSize, grainSize);
+
+ if (size < grainSize * MPS_WORD_WIDTH)
+ /* Not enough room for a full complement of zones. */
+ return ResMEMORY;
clArenaSize = SizeAlignUp(sizeof(ClientArenaStruct), MPS_PF_ALIGN);
if (size < clArenaSize)
@@ -252,14 +293,14 @@ static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args)
arena = ClientArena2Arena(clientArena);
/* */
- res = ArenaInit(arena, class);
+ res = ArenaInit(arena, class, grainSize, args);
if (res != ResOK)
return res;
/* have to have a valid arena before calling ChunkCreate */
clientArena->sig = ClientArenaSig;
- res = clientChunkCreate(&chunk, chunkBase, limit, clientArena);
+ res = clientChunkCreate(&chunk, clientArena, chunkBase, limit);
if (res != ResOK)
goto failChunkCreate;
arena->primary = chunk;
@@ -269,7 +310,7 @@ static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args)
/* bits in a word). Note that some zones are discontiguous in the */
/* arena if the size is not a power of 2. */
arena->zoneShift = SizeFloorLog2(size >> MPS_WORD_SHIFT);
- arena->alignment = ChunkPageSize(arena->primary);
+ AVER(ArenaGrainSize(arena) == ChunkPageSize(arena->primary));
EVENT3(ArenaCreateCL, arena, size, base);
AVERT(ClientArena, clientArena);
@@ -288,19 +329,22 @@ static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args)
static void ClientArenaFinish(Arena arena)
{
ClientArena clientArena;
- Ring node, next;
clientArena = Arena2ClientArena(arena);
AVERT(ClientArena, clientArena);
- /* destroy all chunks */
- RING_FOR(node, &arena->chunkRing, next) {
- Chunk chunk = RING_ELT(Chunk, chunkRing, node);
- clientChunkDestroy(chunk);
- }
+ /* Destroy all chunks, including the primary. See
+ * */
+ arena->primary = NULL;
+ TreeTraverseAndDelete(&arena->chunkTree, clientChunkDestroy,
+ UNUSED_POINTER, UNUSED_SIZE);
clientArena->sig = SigInvalid;
+ /* Destroying the chunks should leave nothing behind. */
+ AVER(arena->reserved == 0);
+ AVER(arena->committed == 0);
+
ArenaFinish(arena); /* */
}
@@ -320,126 +364,43 @@ static Res ClientArenaExtend(Arena arena, Addr base, Size size)
limit = AddrAdd(base, size);
clientArena = Arena2ClientArena(arena);
- res = clientChunkCreate(&chunk, base, limit, clientArena);
+ res = clientChunkCreate(&chunk, clientArena, base, limit);
return res;
}
-/* 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, chunkRing, node);
- AVERT(Chunk, chunk);
- size += AddrOffset(chunk->base, chunk->limit);
- }
-
- return size;
-}
-
-
-/* chunkAlloc -- allocate some tracts in a chunk */
+/* ClientArenaPagesMarkAllocated -- Mark the pages allocated */
-static Res chunkAlloc(Addr *baseReturn, Tract *baseTractReturn,
- SegPref pref, Size pages, Pool pool, Chunk chunk)
+static Res ClientArenaPagesMarkAllocated(Arena arena, Chunk chunk,
+ Index baseIndex, Count pages,
+ Pool pool)
{
- Index baseIndex, limitIndex, indx;
- Bool b;
- Arena arena;
+ Index i;
ClientChunk clChunk;
-
- AVER(baseReturn != NULL);
- AVER(baseTractReturn != NULL);
+
+ AVERT(Arena, arena);
+ AVERT(Chunk, chunk);
clChunk = Chunk2ClientChunk(chunk);
+ AVERT(ClientChunk, clChunk);
+ AVER(chunk->allocBase <= baseIndex);
+ AVER(pages > 0);
+ AVER(baseIndex + pages <= chunk->pages);
+ AVERT(Pool, pool);
- if (pages > clChunk->freePages)
- return ResRESOURCE;
-
- arena = chunk->arena;
-
- if (pref->high)
- b = BTFindShortResRangeHigh(&baseIndex, &limitIndex, chunk->allocTable,
- chunk->allocBase, chunk->pages, pages);
- else
- b = BTFindShortResRange(&baseIndex, &limitIndex, chunk->allocTable,
- chunk->allocBase, chunk->pages, pages);
-
- if (!b)
- return ResRESOURCE;
-
- /* Check commit limit. Note that if there are multiple reasons */
- /* for failing the allocation we attempt to return other result codes */
- /* in preference to ResCOMMIT_LIMIT. See */
- if (ArenaCommitted(arena) + pages * ChunkPageSize(chunk)
- > arena->commitLimit) {
- return ResCOMMIT_LIMIT;
- }
-
- /* Initialize the generic tract structures. */
- AVER(limitIndex > baseIndex);
- for(indx = baseIndex; indx < limitIndex; ++indx) {
- PageAlloc(chunk, indx, pool);
- }
+ for (i = 0; i < pages; ++i)
+ PageAlloc(chunk, baseIndex + i, pool);
+ arena->committed += ChunkPagesToSize(chunk, pages);
+ AVER(clChunk->freePages >= pages);
clChunk->freePages -= pages;
- *baseReturn = PageIndexBase(chunk, baseIndex);
- *baseTractReturn = PageTract(ChunkPage(chunk, baseIndex));
-
return ResOK;
}
-/* ClientAlloc -- allocate a region from the arena */
-
-static Res ClientAlloc(Addr *baseReturn, Tract *baseTractReturn,
- SegPref pref, Size size, Pool pool)
-{
- Arena arena;
- Res res;
- Ring node, nextNode;
- Size pages;
-
- AVER(baseReturn != NULL);
- AVER(baseTractReturn != NULL);
- AVERT(SegPref, pref);
- AVER(size > 0);
- AVERT(Pool, pool);
-
- arena = PoolArena(pool);
- AVERT(Arena, arena);
- /* All chunks have same pageSize. */
- AVER(SizeIsAligned(size, ChunkPageSize(arena->primary)));
- /* NULL is used as a discriminator (see */
- /* ), therefore the real pool */
- /* must be non-NULL. */
- AVER(pool != NULL);
-
- pages = ChunkSizeToPages(arena->primary, size);
-
- /* .req.extend.slow */
- RING_FOR(node, &arena->chunkRing, nextNode) {
- Chunk chunk = RING_ELT(Chunk, chunkRing, node);
- res = chunkAlloc(baseReturn, baseTractReturn, pref, pages, pool, chunk);
- if (res == ResOK || res == ResCOMMIT_LIMIT) {
- return res;
- }
- }
- return ResRESOURCE;
-}
-
-
-/* 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" */
@@ -480,6 +441,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;
}
@@ -495,12 +458,12 @@ DEFINE_ARENA_CLASS(ClientArenaClass, this)
this->varargs = ClientArenaVarargs;
this->init = ClientArenaInit;
this->finish = ClientArenaFinish;
- this->reserved = ClientArenaReserved;
this->extend = ClientArenaExtend;
- this->alloc = ClientAlloc;
- this->free = ClientFree;
+ this->pagesMarkAllocated = ClientArenaPagesMarkAllocated;
+ this->free = ClientArenaFree;
this->chunkInit = ClientChunkInit;
this->chunkFinish = ClientChunkFinish;
+ AVERT(ArenaClass, this);
}
@@ -514,7 +477,7 @@ mps_arena_class_t mps_arena_class_cl(void)
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/arenacv.c b/code/arenacv.c
index b52345f901..7b02c11bcc 100644
--- a/code/arenacv.c
+++ b/code/arenacv.c
@@ -1,7 +1,7 @@
/* arenacv.c: ARENA COVERAGE TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .coverage: At the moment, we're only trying to cover the new code
* (partial mapping of the page table and vm overflow).
@@ -14,8 +14,6 @@
* being allocated; this requires using two adjacent zones.
*/
-#include
-
#include "mpm.h"
#include "poolmv.h"
#include "testlib.h"
@@ -23,6 +21,9 @@
#include "mpsavm.h"
#include "mpsacl.h"
+#include /* printf */
+#include /* malloc */
+
#define tractsSIZE 500
@@ -54,7 +55,7 @@ typedef struct AllocInfoStruct {
} the;
} AllocInfoStruct;
-typedef Res (*AllocFun)(AllocInfoStruct *aiReturn, SegPref pref,
+typedef Res (*AllocFun)(AllocInfoStruct *aiReturn, LocusPref pref,
Size size, Pool pool);
typedef void (*FreeFun)(AllocInfo ai);
@@ -86,9 +87,76 @@ typedef struct AllocatorClassStruct {
} AllocatorClassStruct;
+/* tractSearchInChunk -- find a tract in a chunk
+ *
+ * .tract-search: Searches for a tract in the chunk starting at page
+ * index i, return FALSE if there is none.
+ */
+
+static Bool tractSearchInChunk(Tract *tractReturn, Chunk chunk, Index i)
+{
+ AVER_CRITICAL(chunk->allocBase <= i);
+ AVER_CRITICAL(i <= chunk->pages);
+
+ while (i < chunk->pages
+ && !(BTGet(chunk->allocTable, i)
+ && PageIsAllocated(ChunkPage(chunk, i)))) {
+ ++i;
+ }
+ if (i == chunk->pages)
+ return FALSE;
+ AVER(i < chunk->pages);
+ *tractReturn = PageTract(ChunkPage(chunk, i));
+ return TRUE;
+}
+
+
+/* tractSearch -- find next tract above address
+ *
+ * Searches for the next tract in increasing address order.
+ * The tract returned is the next one along from addr (i.e.,
+ * it has a base address bigger than addr and no other tract
+ * with a base address bigger than addr has a smaller base address).
+ *
+ * Returns FALSE if there is no tract to find (end of the arena).
+ */
+
+static Bool tractSearch(Tract *tractReturn, Arena arena, Addr addr)
+{
+ Bool b;
+ Chunk chunk;
+ Tree tree;
+
+ b = ChunkOfAddr(&chunk, arena, addr);
+ if (b) {
+ Index i;
+
+ i = INDEX_OF_ADDR(chunk, addr);
+ /* There are fewer pages than addresses, therefore the */
+ /* page index can never wrap around */
+ AVER_CRITICAL(i+1 != 0);
+
+ if (tractSearchInChunk(tractReturn, chunk, i+1)) {
+ return TRUE;
+ }
+ }
+ while (TreeFindNext(&tree, ArenaChunkTree(arena), TreeKeyOfAddrVar(addr),
+ ChunkCompare))
+ {
+ chunk = ChunkOfTree(tree);
+ addr = chunk->base;
+ /* Start from allocBase to skip the tables. */
+ if (tractSearchInChunk(tractReturn, chunk, chunk->allocBase)) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
/* Implementation of the tract-based interchangability interface */
-static Res allocAsTract(AllocInfoStruct *aiReturn, SegPref pref,
+static Res allocAsTract(AllocInfoStruct *aiReturn, LocusPref pref,
Size size, Pool pool)
{
Res res;
@@ -113,10 +181,10 @@ static Bool firstAsTract(AllocInfoStruct *aiReturn, Arena arena)
{
Bool res;
Tract tract;
- res = TractFirst(&tract, arena);
+ res = tractSearch(&tract, arena, 0);
if (res) {
aiReturn->the.tractData.base = TractBase(tract);
- aiReturn->the.tractData.size = ArenaAlign(arena);;
+ aiReturn->the.tractData.size = ArenaGrainSize(arena);;
aiReturn->the.tractData.pool = TractPool(tract);
}
return res;
@@ -127,10 +195,10 @@ static Bool nextAsTract(AllocInfoStruct *nextReturn, AllocInfo ai,
{
Bool res;
Tract tract;
- res = TractNext(&tract, arena, ai->the.tractData.base);
+ res = tractSearch(&tract, arena, ai->the.tractData.base);
if (res) {
nextReturn->the.tractData.base = TractBase(tract);
- nextReturn->the.tractData.size = ArenaAlign(arena);;
+ nextReturn->the.tractData.size = ArenaGrainSize(arena);;
nextReturn->the.tractData.pool = TractPool(tract);
}
return res;
@@ -176,7 +244,7 @@ static AllocatorClassStruct allocatorTractStruct = {
/* Implementation of the segment-based interchangability interface */
-static Res allocAsSeg(AllocInfoStruct *aiReturn, SegPref pref,
+static Res allocAsSeg(AllocInfoStruct *aiReturn, LocusPref pref,
Size size, Pool pool)
{
Res res;
@@ -261,12 +329,12 @@ static void testAllocAndIterate(Arena arena, Pool pool,
AllocatorClass allocator)
{
AllocInfoStruct offsetRegion, gapRegion, newRegion, topRegion;
- SegPrefStruct pref;
+ LocusPrefStruct pref;
Count offset, gap, new;
ZoneSet zone = (ZoneSet)2;
int i;
- SegPrefInit(&pref);
+ LocusPrefInit(&pref);
/* Testing the behaviour with various sizes of gaps in the page table. */
@@ -329,13 +397,12 @@ static void testAllocAndIterate(Arena arena, Pool pool,
allocator->free(&offsetRegion);
}
}
- SegPrefExpress(&pref, SegPrefZoneSet, &zone);
+ LocusPrefExpress(&pref, LocusPrefZONESET, &zone);
}
-
}
-static void testPageTable(ArenaClass class, Size size, Addr addr)
+static void testPageTable(ArenaClass class, Size size, Addr addr, Bool zoned)
{
Arena arena; Pool pool;
Size pageSize;
@@ -344,12 +411,13 @@ static void testPageTable(ArenaClass class, Size size, Addr addr)
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, size);
MPS_ARGS_ADD(args, MPS_KEY_ARENA_CL_BASE, addr);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, zoned);
die(ArenaCreate(&arena, class, args), "ArenaCreate");
} MPS_ARGS_END(args);
die(PoolCreate(&pool, arena, PoolClassMV(), argsNone), "PoolCreate");
- pageSize = ArenaAlign(arena);
+ pageSize = ArenaGrainSize(arena);
tractsPerPage = pageSize / sizeof(TractStruct);
printf("%ld tracts per page in the page table.\n", (long)tractsPerPage);
@@ -361,6 +429,10 @@ static void testPageTable(ArenaClass class, Size size, Addr addr)
testAllocAndIterate(arena, pool, pageSize, tractsPerPage,
&allocatorSegStruct);
+ die(ArenaDescribe(arena, mps_lib_get_stdout(), 0), "ArenaDescribe");
+ die(ArenaDescribeTracts(arena, mps_lib_get_stdout(), 0),
+ "ArenaDescribeTracts");
+
PoolDestroy(pool);
ArenaDestroy(arena);
}
@@ -398,14 +470,15 @@ static void testSize(Size size)
int main(int argc, char *argv[])
{
void *block;
- testlib_unused(argc);
- testPageTable((ArenaClass)mps_arena_class_vm(), TEST_ARENA_SIZE, 0);
- testPageTable((ArenaClass)mps_arena_class_vmnz(), TEST_ARENA_SIZE, 0);
+ testlib_init(argc, argv);
+
+ testPageTable((ArenaClass)mps_arena_class_vm(), TEST_ARENA_SIZE, 0, TRUE);
+ testPageTable((ArenaClass)mps_arena_class_vm(), TEST_ARENA_SIZE, 0, FALSE);
block = malloc(TEST_ARENA_SIZE);
cdie(block != NULL, "malloc");
- testPageTable((ArenaClass)mps_arena_class_cl(), TEST_ARENA_SIZE, block);
+ testPageTable((ArenaClass)mps_arena_class_cl(), TEST_ARENA_SIZE, block, FALSE);
testSize(TEST_ARENA_SIZE);
@@ -416,7 +489,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/arenavm.c b/code/arenavm.c
index fae4419a3f..f5666a1e7e 100644
--- a/code/arenavm.c
+++ b/code/arenavm.c
@@ -22,11 +22,14 @@
*/
#include "boot.h"
-#include "tract.h"
#include "bt.h"
-#include "sa.h"
+#include "cbs.h"
#include "mpm.h"
#include "mpsavm.h"
+#include "poolmfs.h"
+#include "sa.h"
+#include "tract.h"
+#include "vm.h"
SRCID(arenavm, "$Id$");
@@ -39,13 +42,14 @@ typedef struct VMChunkStruct *VMChunk;
typedef struct VMChunkStruct {
ChunkStruct chunkStruct; /* generic chunk */
- VM vm; /* virtual memory handle */
+ VMStruct vmStruct; /* virtual memory descriptor */
Addr overheadMappedLimit; /* limit of pages mapped for overhead */
SparseArrayStruct pages; /* to manage backing store of page table */
Sig sig; /* */
} VMChunkStruct;
#define VMChunk2Chunk(vmchunk) (&(vmchunk)->chunkStruct)
+#define VMChunkVM(vmchunk) (&(vmchunk)->vmStruct)
#define Chunk2VMChunk(chunk) PARENT(VMChunkStruct, chunkStruct, chunk)
@@ -66,22 +70,20 @@ typedef struct VMArenaStruct *VMArena;
typedef struct VMArenaStruct { /* VM arena structure */
ArenaStruct arenaStruct;
- VM vm; /* VM where the arena itself is stored */
+ VMStruct vmStruct; /* VM descriptor for VM containing arena */
char vmParams[VMParamSize]; /* VM parameter block */
- Size spareSize; /* total size of spare pages */
- ZoneSet blacklist; /* zones to use last */
- ZoneSet freeSet; /* unassigned zones */
+ Size spareSize; /* total size of spare pages */
Size extendBy; /* desired arena increment */
Size extendMin; /* minimum arena increment */
ArenaVMExtendedCallback extended;
ArenaVMContractedCallback contracted;
RingStruct spareRing; /* spare (free but mapped) tracts */
- RingStruct freeRing[MPS_WORD_WIDTH]; /* free page caches, per zone */
Sig sig; /* */
} VMArenaStruct;
#define Arena2VMArena(arena) PARENT(VMArenaStruct, arenaStruct, arena)
#define VMArena2Arena(vmarena) (&(vmarena)->arenaStruct)
+#define VMArenaVM(vmarena) (&(vmarena)->vmStruct)
/* Forward declarations */
@@ -89,21 +91,21 @@ typedef struct VMArenaStruct { /* VM arena structure */
static Size VMPurgeSpare(Arena arena, Size size);
static void chunkUnmapSpare(Chunk chunk);
extern ArenaClass VMArenaClassGet(void);
-extern ArenaClass VMNZArenaClassGet(void);
static void VMCompact(Arena arena, Trace trace);
/* VMChunkCheck -- check the consistency of a VM chunk */
+ATTRIBUTE_UNUSED
static Bool VMChunkCheck(VMChunk vmchunk)
{
Chunk chunk;
CHECKS(VMChunk, vmchunk);
chunk = VMChunk2Chunk(vmchunk);
- CHECKL(ChunkCheck(chunk));
- CHECKL(VMCheck(vmchunk->vm));
- CHECKL(VMAlign(vmchunk->vm) == ChunkPageSize(chunk));
+ CHECKD(Chunk, chunk);
+ CHECKD(VM, VMChunkVM(vmchunk));
+ CHECKL(SizeIsAligned(ChunkPageSize(chunk), VMPageSize(VMChunkVM(vmchunk))));
CHECKL(vmchunk->overheadMappedLimit <= (Addr)chunk->pageTable);
CHECKD(SparseArray, &vmchunk->pages);
/* SparseArrayCheck is agnostic about where the BTs live, so VMChunkCheck
@@ -154,9 +156,9 @@ static Bool VMChunkCheck(VMChunk vmchunk)
/* VMArenaCheck -- check the consistency of an arena structure */
+ATTRIBUTE_UNUSED
static Bool VMArenaCheck(VMArena vmArena)
{
- Index i;
Arena arena;
VMChunk primary;
@@ -165,7 +167,6 @@ static Bool VMArenaCheck(VMArena vmArena)
CHECKD(Arena, arena);
/* spare pages are committed, so must be less spare than committed. */
CHECKL(vmArena->spareSize <= arena->committed);
- CHECKL(vmArena->blacklist != ZoneSetUNIV);
CHECKL(vmArena->extendBy > 0);
CHECKL(vmArena->extendMin <= vmArena->extendBy);
@@ -175,12 +176,10 @@ static Bool VMArenaCheck(VMArena vmArena)
CHECKD(VMChunk, primary);
/* We could iterate over all chunks accumulating an accurate */
/* count of committed, but we don't have all day. */
- CHECKL(VMMapped(primary->vm) <= arena->committed);
+ CHECKL(VMMapped(VMChunkVM(primary)) <= arena->committed);
}
- CHECKL(RingCheck(&vmArena->spareRing));
- for (i = 0; i < NELEMS(vmArena->freeRing); ++i)
- CHECKL(RingCheck(&vmArena->freeRing[i]));
+ CHECKD_NOSIG(Ring, &vmArena->spareRing);
/* FIXME: Can't check VMParams */
@@ -190,15 +189,18 @@ static Bool VMArenaCheck(VMArena vmArena)
/* VMArenaDescribe -- describe the VMArena
*/
-static Res VMArenaDescribe(Arena arena, mps_lib_FILE *stream)
+static Res VMArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth)
{
Res res;
VMArena vmArena;
- if (!TESTT(Arena, arena)) return ResFAIL;
- if (stream == NULL) return ResFAIL;
+ if (!TESTT(Arena, arena))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
vmArena = Arena2VMArena(arena);
- if (!TESTT(VMArena, vmArena)) return ResFAIL;
+ if (!TESTT(VMArena, vmArena))
+ return ResFAIL;
/* Describe the superclass fields first via next-method call */
/* ...but the next method is ArenaTrivDescribe, so don't call it;
@@ -206,13 +208,13 @@ static Res VMArenaDescribe(Arena arena, mps_lib_FILE *stream)
*
super = ARENA_SUPERCLASS(VMArenaClass);
res = super->describe(arena, stream);
- if (res != ResOK) return res;
+ if (res != ResOK)
+ return res;
*
*/
- res = WriteF(stream,
- " freeSet: $B\n", (WriteFB)vmArena->freeSet,
- " blacklist: $B\n", (WriteFB)vmArena->blacklist,
+ res = WriteF(stream, depth,
+ " spareSize: $U\n", (WriteFU)vmArena->spareSize,
NULL);
if(res != ResOK)
return res;
@@ -280,10 +282,11 @@ static void vmArenaUnmap(VMArena vmArena, VM vm, Addr base, Addr limit)
*/
static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size)
{
+ Arena arena;
Res res;
Addr base, limit, chunkStructLimit;
- Align pageSize;
- VM vm;
+ VMStruct vmStruct;
+ VM vm = &vmStruct;
BootBlockStruct bootStruct;
BootBlock boot = &bootStruct;
VMChunk vmChunk;
@@ -291,14 +294,13 @@ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size)
AVER(chunkReturn != NULL);
AVERT(VMArena, vmArena);
+ arena = VMArena2Arena(vmArena);
AVER(size > 0);
- res = VMCreate(&vm, size, vmArena->vmParams);
+ res = VMInit(vm, size, ArenaGrainSize(arena), vmArena->vmParams);
if (res != ResOK)
- goto failVMCreate;
+ goto failVMInit;
- pageSize = VMAlign(vm);
- /* The VM will have aligned the userSize; pick up the actual size. */
base = VMBase(vm);
limit = VMLimit(vm);
@@ -312,16 +314,17 @@ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size)
if (res != ResOK)
goto failChunkAlloc;
vmChunk = p;
- /* Calculate the limit of the page where the chunkStruct resides. */
- chunkStructLimit = AddrAlignUp((Addr)(vmChunk + 1), pageSize);
+ /* Calculate the limit of the grain where the chunkStruct resides. */
+ chunkStructLimit = AddrAlignUp((Addr)(vmChunk + 1), ArenaGrainSize(arena));
res = vmArenaMap(vmArena, vm, base, chunkStructLimit);
if (res != ResOK)
goto failChunkMap;
vmChunk->overheadMappedLimit = chunkStructLimit;
- vmChunk->vm = vm;
- res = ChunkInit(VMChunk2Chunk(vmChunk), VMArena2Arena(vmArena),
- base, limit, pageSize, boot);
+ /* Copy VM descriptor into its place in the chunk. */
+ VMCopy(VMChunkVM(vmChunk), vm);
+ res = ChunkInit(VMChunk2Chunk(vmChunk), arena, base, limit,
+ VMReserved(VMChunkVM(vmChunk)), boot);
if (res != ResOK)
goto failChunkInit;
@@ -329,16 +332,17 @@ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size)
vmChunk->sig = VMChunkSig;
AVERT(VMChunk, vmChunk);
+
*chunkReturn = VMChunk2Chunk(vmChunk);
return ResOK;
failChunkInit:
- /* No need to unmap, as we're destroying the VM. */
+ VMUnmap(vm, VMBase(vm), chunkStructLimit);
failChunkMap:
failChunkAlloc:
failBootInit:
- VMDestroy(vm);
-failVMCreate:
+ VMFinish(vm);
+failVMInit:
return res;
}
@@ -379,7 +383,7 @@ static Res VMChunkInit(Chunk chunk, BootBlock boot)
/* Map memory for the bit tables. */
if (vmChunk->overheadMappedLimit < overheadLimit) {
overheadLimit = AddrAlignUp(overheadLimit, ChunkPageSize(chunk));
- res = vmArenaMap(VMChunkVMArena(vmChunk), vmChunk->vm,
+ res = vmArenaMap(VMChunkVMArena(vmChunk), VMChunkVM(vmChunk),
vmChunk->overheadMappedLimit, overheadLimit);
if (res != ResOK)
goto failTableMap;
@@ -390,7 +394,7 @@ static Res VMChunkInit(Chunk chunk, BootBlock boot)
chunk->pageTable,
sizeof(PageUnion),
chunk->pages,
- saMapped, saPages, vmChunk->vm);
+ saMapped, saPages, VMChunkVM(vmChunk));
return ResOK;
@@ -405,11 +409,18 @@ static Res VMChunkInit(Chunk chunk, BootBlock boot)
/* vmChunkDestroy -- destroy a VMChunk */
-static void vmChunkDestroy(Chunk chunk)
+static Bool vmChunkDestroy(Tree tree, void *closureP, Size closureS)
{
- VM vm;
+ Chunk chunk;
VMChunk vmChunk;
+ AVERT(Tree, tree);
+ AVER(closureP == UNUSED_POINTER);
+ UNUSED(closureP);
+ AVER(closureS == UNUSED_SIZE);
+ UNUSED(closureS);
+
+ chunk = ChunkOfTree(tree);
AVERT(Chunk, chunk);
vmChunk = Chunk2VMChunk(chunk);
AVERT(VMChunk, vmChunk);
@@ -419,9 +430,9 @@ static void vmChunkDestroy(Chunk chunk)
SparseArrayFinish(&vmChunk->pages);
vmChunk->sig = SigInvalid;
- vm = vmChunk->vm;
ChunkFinish(chunk);
- VMDestroy(vm);
+
+ return TRUE;
}
@@ -429,11 +440,20 @@ static void vmChunkDestroy(Chunk chunk)
static void VMChunkFinish(Chunk chunk)
{
+ VMStruct vmStruct;
+ VM vm = &vmStruct;
VMChunk vmChunk = Chunk2VMChunk(chunk);
- vmArenaUnmap(VMChunkVMArena(vmChunk), vmChunk->vm,
- VMBase(vmChunk->vm), vmChunk->overheadMappedLimit);
+ /* Copy VM descriptor to stack-local storage so that we can continue
+ * using the descriptor after the VM has been unmapped. */
+ VMCopy(vm, VMChunkVM(vmChunk));
+
+ vmArenaUnmap(VMChunkVMArena(vmChunk), vm,
+ VMBase(vm), vmChunk->overheadMappedLimit);
+
/* No point in finishing the other fields, since they are unmapped. */
+
+ VMFinish(vm);
}
@@ -444,7 +464,7 @@ static void VMArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs)
args[0].key = MPS_KEY_ARENA_SIZE;
args[0].val.size = va_arg(varargs, Size);
args[1].key = MPS_KEY_ARGS_END;
- AVER(ArgListCheck(args));
+ AVERT(ArgList, args);
}
@@ -486,87 +506,75 @@ ARG_DEFINE_KEY(arena_contracted, Fun);
static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args)
{
- Size userSize; /* size requested by user */
- Size chunkSize; /* size actually created */
+ Size size = VM_ARENA_SIZE_DEFAULT; /* initial arena size */
+ Align grainSize = MPS_PF_ALIGN; /* arena grain size */
+ Size pageSize = PageSize(); /* operating system page size */
+ Size chunkSize; /* size actually created */
Size vmArenaSize; /* aligned size of VMArenaStruct */
Res res;
VMArena vmArena;
Arena arena;
- VM arenaVM;
+ VMStruct vmStruct;
+ VM vm = &vmStruct;
Chunk chunk;
mps_arg_s arg;
char vmParams[VMParamSize];
- Index i;
AVER(arenaReturn != NULL);
- AVER(class == VMArenaClassGet() || class == VMNZArenaClassGet());
- AVER(ArgListCheck(args));
-
- ArgRequire(&arg, args, MPS_KEY_ARENA_SIZE);
- userSize = arg.val.size;
-
- AVER(userSize > 0);
+ AVER(class == VMArenaClassGet());
+ AVERT(ArgList, args);
+
+ if (ArgPick(&arg, args, MPS_KEY_ARENA_GRAIN_SIZE))
+ grainSize = arg.val.size;
+ if (grainSize < pageSize)
+ /* Make it easier to write portable programs by rounding up. */
+ grainSize = pageSize;
+ AVERT(ArenaGrainSize, grainSize);
+
+ if (ArgPick(&arg, args, MPS_KEY_ARENA_SIZE))
+ size = arg.val.size;
+ if (size < grainSize * MPS_WORD_WIDTH)
+ /* There has to be enough room in the chunk for a full complement of
+ zones. Make it easier to write portable programs by rounding up. */
+ size = grainSize * MPS_WORD_WIDTH;
- /* Parse the arguments into VM parameters, if any. We must do this into
- some stack-allocated memory for the moment, since we don't have anywhere
- else to put it. It gets copied later. */
+ /* Parse remaining arguments, if any, into VM parameters. We must do
+ this into some stack-allocated memory for the moment, since we
+ don't have anywhere else to put it. It gets copied later. */
res = VMParamFromArgs(vmParams, sizeof(vmParams), args);
if (res != ResOK)
- goto failVMCreate;
+ goto failVMInit;
- /* Create a VM to hold the arena and map it. */
+ /* Create a VM to hold the arena and map it. Store descriptor on the
+ stack until we have the arena to put it in. */
vmArenaSize = SizeAlignUp(sizeof(VMArenaStruct), MPS_PF_ALIGN);
- res = VMCreate(&arenaVM, vmArenaSize, vmParams);
+ res = VMInit(vm, vmArenaSize, grainSize, vmParams);
if (res != ResOK)
- goto failVMCreate;
- res = VMMap(arenaVM, VMBase(arenaVM), VMLimit(arenaVM));
+ goto failVMInit;
+ res = VMMap(vm, VMBase(vm), VMLimit(vm));
if (res != ResOK)
goto failVMMap;
- vmArena = (VMArena)VMBase(arenaVM);
+ vmArena = (VMArena)VMBase(vm);
arena = VMArena2Arena(vmArena);
/* */
- res = ArenaInit(arena, class);
+ res = ArenaInit(arena, class, grainSize, args);
if (res != ResOK)
goto failArenaInit;
- arena->committed = VMMapped(arenaVM);
+ arena->reserved = VMReserved(vm);
+ arena->committed = VMMapped(vm);
- vmArena->vm = arenaVM;
+ /* Copy VM descriptor into its place in the arena. */
+ VMCopy(VMArenaVM(vmArena), vm);
vmArena->spareSize = 0;
RingInit(&vmArena->spareRing);
- for (i = 0; i < NELEMS(vmArena->freeRing); ++i)
- RingInit(&vmArena->freeRing[i]);
/* Copy the stack-allocated VM parameters into their home in the VMArena. */
AVER(sizeof(vmArena->vmParams) == sizeof(vmParams));
- mps_lib_memcpy(vmArena->vmParams, vmParams, sizeof(vmArena->vmParams));
+ (void)mps_lib_memcpy(vmArena->vmParams, vmParams, sizeof(vmArena->vmParams));
- /* .blacklist: We blacklist the zones that could be referenced by small
- integers misinterpreted as references. This isn't a perfect simulation,
- but it should catch the common cases. */
- {
- union {
- mps_word_t word;
- mps_addr_t addr;
- int i;
- long l;
- } nono;
- vmArena->blacklist = ZoneSetEMPTY;
- nono.word = 0;
- nono.i = 1;
- vmArena->blacklist = ZoneSetAddAddr(arena, vmArena->blacklist, nono.addr);
- nono.i = -1;
- vmArena->blacklist = ZoneSetAddAddr(arena, vmArena->blacklist, nono.addr);
- nono.l = 1;
- vmArena->blacklist = ZoneSetAddAddr(arena, vmArena->blacklist, nono.addr);
- nono.l = -1;
- vmArena->blacklist = ZoneSetAddAddr(arena, vmArena->blacklist, nono.addr);
- }
- EVENT2(ArenaBlacklistZone, vmArena, vmArena->blacklist);
-
- vmArena->freeSet = ZoneSetUNIV; /* includes blacklist */
/* */
- vmArena->extendBy = userSize;
+ vmArena->extendBy = size;
vmArena->extendMin = 0;
vmArena->extended = vmArenaTrivExtended;
@@ -579,25 +587,21 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args)
/* have to have a valid arena before calling ChunkCreate */
vmArena->sig = VMArenaSig;
- res = VMChunkCreate(&chunk, vmArena, userSize);
+ res = VMChunkCreate(&chunk, vmArena, size);
if (res != ResOK)
goto failChunkCreate;
- arena->primary = chunk;
/* .zoneshift: Set the zone shift to divide the chunk into the same */
/* number of stripes as will fit into a reference set (the number of */
/* bits in a word). Fail if the chunk is so small stripes are smaller */
/* than pages. Note that some zones are discontiguous in the chunk if */
/* the size is not a power of 2. See . */
- chunkSize = AddrOffset(chunk->base, chunk->limit);
+ chunkSize = ChunkSize(chunk);
arena->zoneShift = SizeFloorLog2(chunkSize >> MPS_WORD_SHIFT);
- arena->alignment = chunk->pageSize;
+ AVER(ChunkPageSize(chunk) == ArenaGrainSize(arena));
AVERT(VMArena, vmArena);
- if ((ArenaClass)mps_arena_class_vm() == class)
- EVENT3(ArenaCreateVM, arena, userSize, chunkSize);
- else
- EVENT3(ArenaCreateVMNZ, arena, userSize, chunkSize);
+ EVENT3(ArenaCreateVM, arena, size, chunkSize);
vmArena->extended(arena, chunk->base, chunkSize);
@@ -607,10 +611,10 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args)
failChunkCreate:
ArenaFinish(arena);
failArenaInit:
- VMUnmap(arenaVM, VMBase(arenaVM), VMLimit(arenaVM));
+ VMUnmap(vm, VMBase(vm), VMLimit(vm));
failVMMap:
- VMDestroy(arenaVM);
-failVMCreate:
+ VMFinish(vm);
+failVMInit:
return res;
}
@@ -619,293 +623,37 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args)
static void VMArenaFinish(Arena arena)
{
+ VMStruct vmStruct;
+ VM vm = &vmStruct;
VMArena vmArena;
- Ring node, next;
- VM arenaVM;
- Index i;
vmArena = Arena2VMArena(arena);
AVERT(VMArena, vmArena);
- arenaVM = vmArena->vm;
- /* destroy all chunks, including the primary */
+ EVENT1(ArenaDestroy, vmArena);
+
+ /* Destroy all chunks, including the primary. See
+ * */
arena->primary = NULL;
- RING_FOR(node, &arena->chunkRing, next) {
- Chunk chunk = RING_ELT(Chunk, chunkRing, node);
- vmChunkDestroy(chunk);
- }
+ TreeTraverseAndDelete(&arena->chunkTree, vmChunkDestroy,
+ UNUSED_POINTER, UNUSED_SIZE);
/* Destroying the chunks should have purged and removed all spare pages. */
RingFinish(&vmArena->spareRing);
- for (i = 0; i < NELEMS(vmArena->freeRing); ++i)
- RingFinish(&vmArena->freeRing[i]);
/* Destroying the chunks should leave only the arena's own VM. */
- AVER(arena->committed == VMMapped(arenaVM));
+ AVER(arena->reserved == VMReserved(VMArenaVM(vmArena)));
+ AVER(arena->committed == VMMapped(VMArenaVM(vmArena)));
vmArena->sig = SigInvalid;
ArenaFinish(arena); /* */
- VMUnmap(arenaVM, VMBase(arenaVM), VMLimit(arenaVM));
- VMDestroy(arenaVM);
- EVENT1(ArenaDestroy, vmArena);
-}
-
-
-/* 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, chunkRing, node));
- reserved += VMReserved(vmChunk->vm);
- }
- return reserved;
-}
-
-
-/* pagesFindFreeInArea -- find a range of free pages in a given address range
- *
- * Search for a free run of pages in the free table, between the given
- * base and limit.
- *
- * The downwards arg governs whether we use BTFindShortResRange (if
- * downwards is FALSE) or BTFindShortResRangeHigh (if downwards is
- * TRUE). This _roughly_ corresponds to allocating pages from top down
- * (when downwards is TRUE), at least within an interval. It is used
- * for implementing SegPrefHigh.
- */
-static Bool pagesFindFreeInArea(Index *baseReturn, Chunk chunk, Size size,
- Addr base, Addr limit, Bool downwards)
-{
- Word pages; /* number of pages equiv. to size */
- Index basePI, limitPI; /* Index equiv. to base and limit */
- Index start, end; /* base and limit of free run */
-
- AVER(AddrIsAligned(base, ChunkPageSize(chunk)));
- AVER(AddrIsAligned(limit, ChunkPageSize(chunk)));
- AVER(chunk->base <= base);
- AVER(base < limit);
- AVER(limit <= chunk->limit);
- AVER(size <= AddrOffset(base, limit));
- AVER(size > (Size)0);
- AVER(SizeIsAligned(size, ChunkPageSize(chunk)));
-
- basePI = INDEX_OF_ADDR(chunk, base);
- limitPI = INDEX_OF_ADDR(chunk, limit);
- pages = ChunkSizeToPages(chunk, size);
-
- if (downwards) {
- if (!BTFindShortResRangeHigh(&start, &end, chunk->allocTable,
- basePI, limitPI, pages))
- return FALSE;
- } else {
- if(!BTFindShortResRange(&start, &end, chunk->allocTable,
- basePI, limitPI, pages))
- return FALSE;
- }
-
- *baseReturn = start;
- return TRUE;
-}
-
-
-/* pagesFindFreeInZones -- find a range of free pages with a ZoneSet
- *
- * This function finds the intersection of ZoneSet and the set of free
- * pages and tries to find a free run of pages in the resulting set of
- * areas.
- *
- * In other words, it finds space for a page whose ZoneSet (see
- * ZoneSetOfPage) will be a subset of the specified ZoneSet.
- *
- * For meaning of downwards arg see pagesFindFreeInArea.
- * .improve.findfree.downwards: This should be improved so that it
- * allocates pages from top down globally, as opposed to (currently)
- * just within an interval.
- */
-static Bool pagesFindFreeInZones(Index *baseReturn, VMChunk *chunkReturn,
- VMArena vmArena, Size size, ZoneSet zones,
- Bool downwards)
-{
- Arena arena;
- Addr chunkBase, base, limit;
- Size zoneSize;
- Ring node, next;
-
- arena = VMArena2Arena(vmArena);
- zoneSize = (Size)1 << arena->zoneShift;
-
- /* Try to reuse single pages from the already-mapped spare pages list */
- if (size == ArenaAlign(arena)) {
- Index i;
- for (i = 0; i < NELEMS(vmArena->freeRing); ++i) {
- Ring ring = &vmArena->freeRing[i];
- if (ZoneSetIsMember(zones, i) && !RingIsSingle(ring)) {
- Page page = PageOfFreeRing(RingNext(ring));
- Chunk chunk;
- Bool b = ChunkOfAddr(&chunk, arena, (Addr)page);
- AVER(b);
- *baseReturn = (Index)(page - chunk->pageTable);
- *chunkReturn = Chunk2VMChunk(chunk);
- return TRUE;
- }
- }
- }
-
- /* Should we check chunk cache first? */
- RING_FOR(node, &arena->chunkRing, next) {
- Chunk chunk = RING_ELT(Chunk, chunkRing, node);
- AVERT(Chunk, chunk);
-
- /* .alloc.skip: The first address available for arena allocation, */
- /* is just after the arena tables. */
- chunkBase = PageIndexBase(chunk, chunk->allocBase);
-
- base = chunkBase;
- while(base < chunk->limit) {
- if (ZoneSetHasAddr(arena, zones, base)) {
- /* Search for a run of zone stripes which are in the ZoneSet */
- /* and the arena. Adding the zoneSize might wrap round (to */
- /* zero, because limit is aligned to zoneSize, which is a */
- /* power of two). */
- limit = base;
- do {
- /* advance limit to next higher zone stripe boundary */
- limit = AddrAlignUp(AddrAdd(limit, 1), zoneSize);
-
- AVER(limit > base || limit == (Addr)0);
-
- if (limit >= chunk->limit || limit < base) {
- limit = chunk->limit;
- break;
- }
-
- AVER(base < limit);
- AVER(limit < chunk->limit);
- } while(ZoneSetHasAddr(arena, zones, limit));
-
- /* If the ZoneSet was universal, then the area found ought to */
- /* be the whole chunk. */
- AVER(zones != ZoneSetUNIV
- || (base == chunkBase && limit == chunk->limit));
-
- /* Try to allocate a page in the area. */
- if (AddrOffset(base, limit) >= size
- && pagesFindFreeInArea(baseReturn, chunk, size, base, limit,
- downwards)) {
- *chunkReturn = Chunk2VMChunk(chunk);
- return TRUE;
- }
-
- base = limit;
- } else {
- /* Adding the zoneSize might wrap round (to zero, because */
- /* base is aligned to zoneSize, which is a power of two). */
- base = AddrAlignUp(AddrAdd(base, 1), zoneSize);
- AVER(base > chunkBase || base == (Addr)0);
- if (base >= chunk->limit || base < chunkBase) {
- base = chunk->limit;
- break;
- }
- }
- }
-
- AVER(base == chunk->limit);
- }
-
- return FALSE;
-}
-
-
-/* pagesFindFreeWithSegPref -- find a range of free pages with given preferences
- *
- * Note this does not create or allocate any pages.
- *
- * basereturn: return parameter for the index in the
- * chunk's page table of the base of the free area found.
- * chunkreturn: return parameter for the chunk in which
- * the free space has been found.
- * pref: the SegPref object to be used when considering
- * which zones to try.
- * size: Size to find space for.
- * barge: TRUE iff stealing space in zones used
- * by other SegPrefs should be considered (if it's FALSE then only
- * zones already used by this segpref or free zones will be used).
- */
-static Bool pagesFindFreeWithSegPref(Index *baseReturn, VMChunk *chunkReturn,
- VMArena vmArena, SegPref pref, Size size,
- Bool barge)
-{
- /* Some of these tests might be duplicates. If we're about */
- /* to run out of virtual address space, then slow allocation is */
- /* probably the least of our worries. */
-
- /* .alloc.improve.map: Define a function that takes a list */
- /* (say 4 long) of ZoneSets and tries pagesFindFreeInZones on */
- /* each one in turn. Extra ZoneSet args that weren't needed */
- /* could be ZoneSetEMPTY */
-
- if (pref->isCollected) { /* GC'd memory */
- /* We look for space in the following places (in order) */
- /* - Zones already allocated to me (preferred) but are not */
- /* blacklisted; */
- /* - Zones that are either allocated to me, or are unallocated */
- /* but not blacklisted; */
- /* - Any non-blacklisted zone; */
- /* - Any zone; */
- /* Note that each is a superset of the previous, unless */
- /* blacklisted zones have been allocated (or the default */
- /* is used). */
- if (pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size,
- ZoneSetDiff(pref->zones, vmArena->blacklist),
- pref->high)
- || pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size,
- ZoneSetUnion(pref->zones,
- ZoneSetDiff(vmArena->freeSet,
- vmArena->blacklist)),
- pref->high)) {
- return TRUE; /* found */
- }
- if (!barge)
- /* do not barge into other zones, give up now */
- return FALSE;
- if (pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size,
- ZoneSetDiff(ZoneSetUNIV, vmArena->blacklist),
- pref->high)
- || pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size,
- ZoneSetUNIV, pref->high)) {
- return TRUE; /* found */
- }
- } else { /* non-GC'd memory */
- /* We look for space in the following places (in order) */
- /* - Zones preferred (preferred) and blacklisted; */
- /* - Zones preferred; */
- /* - Zones preferred or blacklisted zone; */
- /* - Any zone. */
- /* Note that each is a superset of the previous, unless */
- /* blacklisted zones have been allocated. */
- if (pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size,
- ZoneSetInter(pref->zones, vmArena->blacklist),
- pref->high)
- || pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size,
- pref->zones, pref->high)
- || pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size,
- ZoneSetUnion(pref->zones, vmArena->blacklist),
- pref->high)
- || pagesFindFreeInZones(baseReturn, chunkReturn, vmArena, size,
- ZoneSetUNIV, pref->high)) {
- return TRUE;
- }
- }
- return FALSE;
+ /* Copy VM descriptor to stack-local storage so that we can continue
+ * using the descriptor after the VM has been unmapped. */
+ VMCopy(vm, VMArenaVM(vmArena));
+ VMUnmap(vm, VMBase(vm), VMLimit(vm));
+ VMFinish(vm);
}
@@ -937,20 +685,29 @@ static Size vmArenaChunkSize(VMArena vmArena, Size size)
}
-/* vmArenaExtend -- Extend the arena by making a new chunk
+/* VMArenaGrow -- Extend the arena by making a new chunk
*
* The size arg specifies how much we wish to allocate after the extension.
*/
-static Res vmArenaExtend(VMArena vmArena, Size size)
+static Res VMArenaGrow(Arena arena, LocusPref pref, Size size)
{
Chunk newChunk;
Size chunkSize;
Res res;
+ VMArena vmArena;
+
+ AVERT(Arena, arena);
+ vmArena = Arena2VMArena(arena);
+ AVERT(VMArena, vmArena);
+
+ /* TODO: Ensure that extended arena will be able to satisfy pref. */
+ AVERT(LocusPref, pref);
+ UNUSED(pref);
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 */
{
@@ -964,6 +721,7 @@ static Res vmArenaExtend(VMArena vmArena, Size size)
if (chunkSize < chunkMin)
chunkSize = chunkMin;
+ res = ResRESOURCE;
for(;; chunkSize = chunkHalf) {
chunkHalf = chunkSize / 2;
sliceSize = chunkHalf / fidelity;
@@ -973,66 +731,26 @@ static Res vmArenaExtend(VMArena vmArena, Size size)
for(; chunkSize > chunkHalf; chunkSize -= sliceSize) {
if(chunkSize < chunkMin) {
EVENT2(vmArenaExtendFail, chunkMin,
- VMArenaReserved(VMArena2Arena(vmArena)));
- return ResRESOURCE;
+ ArenaReserved(VMArena2Arena(vmArena)));
+ return res;
}
res = VMChunkCreate(&newChunk, vmArena, chunkSize);
if(res == ResOK)
- goto vmArenaExtend_Done;
+ goto vmArenaGrow_Done;
}
}
}
-
-vmArenaExtend_Done:
- EVENT2(vmArenaExtendDone, chunkSize, VMArenaReserved(VMArena2Arena(vmArena)));
+
+vmArenaGrow_Done:
+ EVENT2(vmArenaExtendDone, chunkSize, ArenaReserved(VMArena2Arena(vmArena)));
vmArena->extended(VMArena2Arena(vmArena),
newChunk->base,
AddrOffset(newChunk->base, newChunk->limit));
-
+
return res;
}
-/* VM*AllocPolicy -- allocation policy methods */
-
-
-/* Used in abstracting allocation policy between VM and VMNZ */
-typedef Res (*VMAllocPolicyMethod)(Index *, VMChunk *, VMArena, SegPref, Size);
-
-static Res VMAllocPolicy(Index *baseIndexReturn, VMChunk *chunkReturn,
- VMArena vmArena, SegPref pref, Size size)
-{
- if (!pagesFindFreeWithSegPref(baseIndexReturn, chunkReturn,
- vmArena, pref, size, FALSE)) {
- /* try and extend, but don't worry if we can't */
- (void)vmArenaExtend(vmArena, size);
-
- /* We may or may not have a new chunk at this point */
- /* we proceed to try the allocation again anyway. */
- /* We specify barging, but if we have got a new chunk */
- /* then hopefully we won't need to barge. */
- if (!pagesFindFreeWithSegPref(baseIndexReturn, chunkReturn,
- vmArena, pref, size, TRUE)) {
- /* .improve.alloc-fail: This could be because the request was */
- /* too large, or perhaps the arena is fragmented. We could */
- /* return a more meaningful code. */
- return ResRESOURCE;
- }
- }
- return ResOK;
-}
-
-static Res VMNZAllocPolicy(Index *baseIndexReturn, VMChunk *chunkReturn,
- VMArena vmArena, SegPref pref, Size size)
-{
- if (pagesFindFreeInZones(baseIndexReturn, chunkReturn, vmArena, size,
- ZoneSetUNIV, pref->high)) {
- return ResOK;
- }
- return ResRESOURCE;
-}
-
-
/* pageState -- determine page state, even if unmapped
*
* Parts of the page table may be unmapped if their corresponding pages are
@@ -1064,25 +782,31 @@ static void sparePageRelease(VMChunk vmChunk, Index pi)
arena->spareCommitted -= ChunkPageSize(chunk);
RingRemove(PageSpareRing(page));
- RingRemove(PageFreeRing(page));
}
static Res pageDescMap(VMChunk vmChunk, Index basePI, Index limitPI)
{
- Size before = VMMapped(vmChunk->vm);
+ Size before = VMMapped(VMChunkVM(vmChunk));
Arena arena = VMArena2Arena(VMChunkVMArena(vmChunk));
Res res = SparseArrayMap(&vmChunk->pages, basePI, limitPI);
- arena->committed += VMMapped(vmChunk->vm) - before;
+ Size after = VMMapped(VMChunkVM(vmChunk));
+ AVER(before <= after);
+ arena->committed += after - before;
return res;
}
static void pageDescUnmap(VMChunk vmChunk, Index basePI, Index limitPI)
{
- Size before = VMMapped(vmChunk->vm);
+ Size size, after;
+ Size before = VMMapped(VMChunkVM(vmChunk));
Arena arena = VMArena2Arena(VMChunkVMArena(vmChunk));
SparseArrayUnmap(&vmChunk->pages, basePI, limitPI);
- arena->committed += VMMapped(vmChunk->vm) - before;
+ after = VMMapped(VMChunkVM(vmChunk));
+ AVER(after <= before);
+ size = before - after;
+ AVER(arena->committed >= size);
+ arena->committed -= size;
}
@@ -1112,7 +836,7 @@ static Res pagesMarkAllocated(VMArena vmArena, VMChunk vmChunk,
res = pageDescMap(vmChunk, j, k);
if (res != ResOK)
goto failSAMap;
- res = vmArenaMap(vmArena, vmChunk->vm,
+ res = vmArenaMap(vmArena, VMChunkVM(vmChunk),
PageIndexBase(chunk, j), PageIndexBase(chunk, k));
if (res != ResOK)
goto failVMMap;
@@ -1136,7 +860,7 @@ static Res pagesMarkAllocated(VMArena vmArena, VMChunk vmChunk,
/* region from basePI to j needs deallocating */
/* TODO: Consider making pages spare instead, then purging. */
if (basePI < j) {
- vmArenaUnmap(vmArena, vmChunk->vm,
+ vmArenaUnmap(vmArena, VMChunkVM(vmChunk),
PageIndexBase(chunk, basePI),
PageIndexBase(chunk, j));
for (i = basePI; i < j; ++i)
@@ -1146,113 +870,42 @@ static Res pagesMarkAllocated(VMArena vmArena, VMChunk vmChunk,
return res;
}
-
-/* vmAllocComm -- allocate a region from the arena
- *
- * Common code used by mps_arena_class_vm and
- * mps_arena_class_vmnz.
- */
-static Res vmAllocComm(Addr *baseReturn, Tract *baseTractReturn,
- VMAllocPolicyMethod policy,
- SegPref pref, Size size, Pool pool)
+static Res VMPagesMarkAllocated(Arena arena, Chunk chunk,
+ Index baseIndex, Count pages, Pool pool)
{
- Addr base, limit;
- Tract baseTract;
- Arena arena;
- Count pages;
- Index baseIndex;
- ZoneSet zones;
Res res;
- VMArena vmArena;
- VMChunk vmChunk;
- Chunk chunk;
- AVER(baseReturn != NULL);
- AVER(baseTractReturn != NULL);
- AVER(FunCheck((Fun)policy));
- AVERT(SegPref, pref);
- AVER(size > (Size)0);
+ AVERT(Arena, arena);
+ AVERT(Chunk, chunk);
+ AVER(chunk->allocBase <= baseIndex);
+ AVER(pages > 0);
+ AVER(baseIndex + pages <= chunk->pages);
AVERT(Pool, pool);
- arena = PoolArena(pool);
- vmArena = Arena2VMArena(arena);
- AVERT(VMArena, vmArena);
- /* All chunks have same pageSize. */
- AVER(SizeIsAligned(size, ChunkPageSize(arena->primary)));
-
- /* NULL is used as a discriminator */
- /* (see ) therefore the real pool */
- /* must be non-NULL. */
- AVER(pool != NULL);
-
- /* Early check on commit limit. */
- if (arena->spareCommitted < size) {
- Size necessaryCommitIncrease = size - arena->spareCommitted;
- if (arena->committed + necessaryCommitIncrease > arena->commitLimit
- || arena->committed + necessaryCommitIncrease < arena->committed) {
- return ResCOMMIT_LIMIT;
- }
- }
-
- res = (*policy)(&baseIndex, &vmChunk, vmArena, pref, size);
- if (res != ResOK)
- return res;
-
- /* chunk (and baseIndex) should be initialised by policy */
- AVERT(VMChunk, vmChunk);
- chunk = VMChunk2Chunk(vmChunk);
-
- /* Compute number of pages to be allocated. */
- pages = ChunkSizeToPages(chunk, size);
-
- res = pagesMarkAllocated(vmArena, vmChunk, baseIndex, pages, pool);
+ res = pagesMarkAllocated(Arena2VMArena(arena),
+ Chunk2VMChunk(chunk),
+ baseIndex,
+ pages,
+ pool);
+ /* TODO: Could this loop be pushed down into vmArenaMap? */
while (res != ResOK) {
/* Try purging spare pages in the hope that the OS will give them back
at the new address. Will eventually run out of spare pages, so this
loop will terminate. */
/* TODO: Investigate implementing VMRemap so that we can guarantee
success if we have enough spare pages. */
- if (VMPurgeSpare(arena, size) == 0)
- goto failPagesMap;
- res = pagesMarkAllocated(vmArena, vmChunk, baseIndex, pages, pool);
+ if (VMPurgeSpare(arena, pages * ChunkPageSize(chunk)) == 0)
+ break;
+ res = pagesMarkAllocated(Arena2VMArena(arena),
+ Chunk2VMChunk(chunk),
+ baseIndex,
+ pages,
+ pool);
}
-
- base = PageIndexBase(chunk, baseIndex);
- baseTract = PageTract(ChunkPage(chunk, baseIndex));
- limit = AddrAdd(base, size);
- zones = ZoneSetOfRange(arena, base, limit);
-
- if (ZoneSetInter(vmArena->freeSet, zones) != ZoneSetEMPTY) {
- EVENT2(ArenaUseFreeZone, arena, ZoneSetInter(vmArena->freeSet, zones));
- }
- vmArena->freeSet = ZoneSetDiff(vmArena->freeSet, zones);
-
- *baseReturn = base;
- *baseTractReturn = baseTract;
- return ResOK;
-
-failPagesMap:
return res;
}
-static Res VMAlloc(Addr *baseReturn, Tract *baseTractReturn,
- SegPref pref, Size size, Pool pool)
-{
- /* All checks performed in common vmAllocComm */
- return vmAllocComm(baseReturn, baseTractReturn,
- VMAllocPolicy, pref, size, pool);
-}
-
-static Res VMNZAlloc(Addr *baseReturn, Tract *baseTractReturn,
- SegPref pref, Size size, Pool pool)
-{
- /* All checks performed in common vmAllocComm */
- return vmAllocComm(baseReturn, baseTractReturn,
- VMNZAllocPolicy, pref, size, pool);
-}
-
-
/* chunkUnmapAroundPage -- unmap spare pages in a chunk including this one
*
* Unmap the spare page passed, and possibly other pages in the chunk,
@@ -1300,7 +953,7 @@ static Size chunkUnmapAroundPage(Chunk chunk, Size size, Page page)
}
vmArenaUnmap(VMChunkVMArena(vmChunk),
- vmChunk->vm,
+ VMChunkVM(vmChunk),
PageIndexBase(chunk, basePI),
PageIndexBase(chunk, limitPI));
@@ -1317,8 +970,6 @@ static Size chunkUnmapAroundPage(Chunk chunk, Size size, Page page)
* unmapped.
*/
-#define ArenaChunkRing(arena) (&(arena)->chunkRing)
-
static Size arenaUnmapSpare(Arena arena, Size size, Chunk filter)
{
Ring node;
@@ -1341,7 +992,7 @@ static Size arenaUnmapSpare(Arena arena, Size size, Chunk filter)
while (RingNext(node) != &vmArena->spareRing && purged < size) {
Ring next = RingNext(node);
Page page = PageOfSpareRing(next);
- Chunk chunk;
+ Chunk chunk = NULL; /* suppress uninit warning */
Bool b;
/* Use the fact that the page table resides in the chunk to find the
chunk that owns the page. */
@@ -1370,9 +1021,7 @@ static Size VMPurgeSpare(Arena arena, Size size)
static void chunkUnmapSpare(Chunk chunk)
{
AVERT(Chunk, chunk);
- (void)arenaUnmapSpare(ChunkArena(chunk),
- AddrOffset(chunk->base, chunk->limit),
- chunk);
+ (void)arenaUnmapSpare(ChunkArena(chunk), ChunkSize(chunk), chunk);
}
@@ -1423,16 +1072,14 @@ static void VMFree(Addr base, Size size, Pool pool)
tract and will contain junk. */
RingInit(PageSpareRing(page));
RingAppend(&vmArena->spareRing, PageSpareRing(page));
- RingInit(PageFreeRing(page));
- RingInsert(&vmArena->freeRing[AddrZone(arena, PageIndexBase(chunk, pi))],
- PageFreeRing(page));
}
arena->spareCommitted += ChunkPagesToSize(chunk, piLimit - piBase);
BTResRange(chunk->allocTable, piBase, piLimit);
/* Consider returning memory to the OS. */
- /* TODO: Chunks are only destroyed when ArenaCompact is called, and that is
- only called from TraceReclaim. Should consider destroying chunks here. */
+ /* TODO: Chunks are only destroyed when ArenaCompact is called, and
+ that is only called from traceReclaim. Should consider destroying
+ chunks here. See job003815. */
if (arena->spareCommitted > arena->spareCommitLimit) {
/* Purge half of the spare memory, not just the extra sliver, so
that we return a reasonable amount of memory in one go, and avoid
@@ -1445,43 +1092,60 @@ static void VMFree(Addr base, Size size, Pool pool)
}
-static void VMCompact(Arena arena, Trace trace)
+/* vmChunkCompact -- delete chunk if empty and not primary */
+
+static Bool vmChunkCompact(Tree tree, void *closureP, Size closureS)
{
+ Chunk chunk;
+ Arena arena = closureP;
VMArena vmArena;
- Ring node, next;
- Size vmem1;
+
+ AVERT(Tree, tree);
+ AVERT(Arena, arena);
+ AVER(closureS == UNUSED_SIZE);
+ UNUSED(closureS);
vmArena = Arena2VMArena(arena);
AVERT(VMArena, vmArena);
- AVERT(Trace, trace);
+ chunk = ChunkOfTree(tree);
+ AVERT(Chunk, chunk);
+ if(chunk != arena->primary
+ && BTIsResRange(chunk->allocTable, 0, chunk->pages))
+ {
+ Addr base = chunk->base;
+ Size size = ChunkSize(chunk);
+ /* Callback before destroying the chunk, as the arena is (briefly)
+ invalid afterwards. See job003893. */
+ (*vmArena->contracted)(arena, base, size);
+ vmChunkDestroy(tree, UNUSED_POINTER, UNUSED_SIZE);
+ return TRUE;
+ } else {
+ /* Keep this chunk. */
+ return FALSE;
+ }
+}
- vmem1 = VMArenaReserved(arena);
- /* Destroy any empty chunks (except the primary). */
- /* TODO: Avoid a scan of the allocTable by keeping a count of allocated
- pages in a chunk. */
- /* TODO: Avoid oscillations in chunk creation by adding some hysteresis. */
- RING_FOR(node, &arena->chunkRing, next) {
- Chunk chunk = RING_ELT(Chunk, chunkRing, node);
- if(chunk != arena->primary
- && BTIsResRange(chunk->allocTable, 0, chunk->pages)) {
- Addr base = chunk->base;
- Size size = AddrOffset(chunk->base, chunk->limit);
+static void VMCompact(Arena arena, Trace trace)
+{
+ VMArena vmArena;
+ Size vmem1;
- /* Ensure there are no spare (mapped) pages left in the chunk.
- This could be short-cut if we're about to destroy the chunk,
- provided we can do the correct accounting in the arena. */
- chunkUnmapSpare(chunk);
+ vmArena = Arena2VMArena(arena);
+ AVERT(VMArena, vmArena);
+ AVERT(Trace, trace);
- vmChunkDestroy(chunk);
+ vmem1 = ArenaReserved(arena);
- vmArena->contracted(arena, base, size);
- }
- }
+ /* Destroy chunks that are completely free, but not the primary
+ * chunk. See
+ * TODO: add hysteresis here. See job003815. */
+ TreeTraverseAndDelete(&arena->chunkTree, vmChunkCompact, arena,
+ UNUSED_SIZE);
{
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 */
@@ -1508,11 +1172,8 @@ mps_res_t mps_arena_vm_growth(mps_arena_t mps_arena,
vmArena = Arena2VMArena(arena);
AVERT(VMArena, vmArena);
- if(desired < minimum) {
- /* May not desire an increment smaller than the minimum! */
- ArenaLeave(arena);
- return MPS_RES_PARAM;
- }
+ /* Must desire at least the minimum increment! */
+ AVER(desired >= minimum);
vmArena->extendBy = desired;
vmArena->extendMin = minimum;
@@ -1534,26 +1195,15 @@ DEFINE_ARENA_CLASS(VMArenaClass, this)
this->varargs = VMArenaVarargs;
this->init = VMArenaInit;
this->finish = VMArenaFinish;
- this->reserved = VMArenaReserved;
this->purgeSpare = VMPurgeSpare;
- this->alloc = VMAlloc;
+ this->grow = VMArenaGrow;
this->free = VMFree;
this->chunkInit = VMChunkInit;
this->chunkFinish = VMChunkFinish;
this->compact = VMCompact;
this->describe = VMArenaDescribe;
-}
-
-
-/* VMNZArenaClass -- The VMNZ arena class definition
- *
- * VMNZ is just VMArena with a different allocation policy.
- */
-DEFINE_ARENA_CLASS(VMNZArenaClass, this)
-{
- INHERIT_CLASS(this, VMArenaClass);
- this->name = "VMNZ";
- this->alloc = VMNZAlloc;
+ this->pagesMarkAllocated = VMPagesMarkAllocated;
+ AVERT(ArenaClass, this);
}
@@ -1565,14 +1215,6 @@ mps_arena_class_t mps_arena_class_vm(void)
}
-/* mps_arena_class_vmnz -- return the arena class VMNZ */
-
-mps_arena_class_t mps_arena_class_vmnz(void)
-{
- return (mps_arena_class_t)VMNZArenaClassGet();
-}
-
-
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2014 Ravenbrook Limited .
diff --git a/code/arg.c b/code/arg.c
index caced7e6f1..4721c415cc 100644
--- a/code/arg.c
+++ b/code/arg.c
@@ -1,7 +1,7 @@
/* arg.c: ARGUMENT LISTS
*
* $Id$
- * Copyright (c) 2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license.
*
* .source: See .
*/
@@ -52,7 +52,7 @@ Bool ArgCheckAddr(Arg arg) {
}
Bool ArgCheckPoolDebugOptions(Arg arg) {
- CHECKL(PoolDebugOptionsCheck((PoolDebugOptions)arg->val.pool_debug_options));
+ CHECKD_NOSIG(PoolDebugOptions, (PoolDebugOptions)arg->val.pool_debug_options);
return TRUE;
}
@@ -99,8 +99,13 @@ Bool ArgCheckdouble(Arg arg) {
return TRUE;
}
+Bool ArgCheckPool(Arg arg) {
+ CHECKD(Pool, arg->val.pool);
+ return TRUE;
+}
-ARG_DEFINE_KEY(args_end, Shouldnt);
+
+ARG_DEFINE_KEY(ARGS_END, Shouldnt);
ArgStruct mps_args_none[] = {{MPS_KEY_ARGS_END, {0}}};
@@ -133,7 +138,7 @@ Bool ArgListCheck(ArgList args)
CHECKL(args != NULL);
for (i = 0; args[i].key != MPS_KEY_ARGS_END; ++i) {
CHECKL(i < MPS_ARGS_MAX);
- CHECKL(ArgCheck(&args[i]));
+ CHECKD_NOSIG(Arg, &args[i]);
}
return TRUE;
}
@@ -145,7 +150,7 @@ Bool ArgPick(ArgStruct *argOut, ArgList args, Key key) {
Index i;
AVER(argOut != NULL);
- AVER(ArgListCheck(args));
+ AVERT(ArgList, args);
AVERT(Key, key);
for (i = 0; args[i].key != MPS_KEY_ARGS_END; ++i)
@@ -154,6 +159,7 @@ Bool ArgPick(ArgStruct *argOut, ArgList args, Key key) {
return FALSE;
found:
+ AVERT(Arg, &args[i]);
*argOut = args[i];
for(;;) {
args[i] = args[i + 1];
@@ -168,9 +174,8 @@ Bool ArgPick(ArgStruct *argOut, ArgList args, Key key) {
/* ArgRequire -- take a required argument out of the argument list by keyword */
void ArgRequire(ArgStruct *argOut, ArgList args, Key key) {
- if (ArgPick(argOut, args, key))
- return;
- NOTREACHED;
+ Bool b = ArgPick(argOut, args, key);
+ ASSERT(b, key->name);
}
@@ -180,14 +185,14 @@ void ArgTrivVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs)
{
UNUSED(varargs);
args[0].key = MPS_KEY_ARGS_END;
- AVER(ArgListCheck(args));
+ AVERT(ArgList, args);
}
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/arg.h b/code/arg.h
index 1598707355..2b5b3fbb31 100644
--- a/code/arg.h
+++ b/code/arg.h
@@ -1,7 +1,7 @@
/* arg.h: Keyword argument lists
*
* $Id$
- * Copyright (c) 2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license.
*
* .source: See .
*/
@@ -28,7 +28,8 @@ typedef struct mps_key_s {
} KeyStruct;
#define ARG_DEFINE_KEY(id, type) \
- const KeyStruct _mps_key_##id = {KeySig, #id, ArgCheck##type}
+ extern const KeyStruct _mps_key_##id; \
+ const KeyStruct _mps_key_##id = {KeySig, "MPS_KEY_" #id, ArgCheck##type}
#define argsNone mps_args_none
@@ -54,6 +55,7 @@ extern Bool ArgCheckPointer(Arg arg);
extern Bool ArgCheckRankSet(Arg arg);
extern Bool ArgCheckRank(Arg arg);
extern Bool ArgCheckdouble(Arg arg);
+extern Bool ArgCheckPool(Arg arg);
#endif /* arg_h */
@@ -61,7 +63,7 @@ extern Bool ArgCheckdouble(Arg arg);
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/awlut.c b/code/awlut.c
index 25ceb34b29..464a2dba91 100644
--- a/code/awlut.c
+++ b/code/awlut.c
@@ -1,7 +1,7 @@
/* awlut.c: POOL CLASS AWL UNIT TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* DESIGN
*
@@ -16,10 +16,9 @@
#include "mpslib.h"
#include "mps.h"
#include "mpstd.h"
-#ifdef MPS_OS_W3
-#include "mpsw3.h"
-#endif
-#include
+
+#include /* printf */
+#include /* strlen */
#define testArenaSIZE ((size_t)64<<20)
@@ -118,7 +117,7 @@ static mps_word_t *alloc_string(const char *s, mps_ap_t ap)
* .assume.dylan-obj
*/
-static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap)
+static mps_word_t *alloc_table(size_t n, mps_ap_t ap)
{
size_t objsize;
void *p;
@@ -127,7 +126,7 @@ static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap)
objsize = (3 + n) * sizeof(mps_word_t);
objsize = size_tAlignUp(objsize, MPS_PF_ALIGN);
do {
- unsigned long i;
+ size_t i;
die(mps_reserve(&p, ap, objsize), "Reserve Table\n");
object = p;
@@ -145,7 +144,7 @@ static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap)
/* gets the nth slot from a table
* .assume.dylan-obj
*/
-static mps_word_t *table_slot(mps_word_t *table, unsigned long n)
+static mps_word_t *table_slot(mps_word_t *table, size_t n)
{
return (mps_word_t *)table[3+n];
}
@@ -154,8 +153,7 @@ static mps_word_t *table_slot(mps_word_t *table, unsigned long n)
/* sets the nth slot in a table
* .assume.dylan-obj
*/
-static void set_table_slot(mps_word_t *table,
- unsigned long n, mps_word_t *p)
+static void set_table_slot(mps_word_t *table, size_t n, mps_word_t *p)
{
cdie(table[0] == (mps_word_t)table_wrapper, "set_table_slot");
table[3+n] = (mps_word_t)p;
@@ -182,7 +180,7 @@ static void test(mps_arena_t arena,
mps_word_t *exacttable;
mps_word_t *preserve[TABLE_SLOTS]; /* preserves objects in the weak */
/* table by referring to them */
- unsigned long i, j;
+ size_t i, j;
void *p;
exacttable = alloc_table(TABLE_SLOTS, exactap);
@@ -194,11 +192,12 @@ static void test(mps_arena_t arena,
for(i = 0; i < TABLE_SLOTS; ++i) {
mps_word_t *string;
- /* Ensure that the last entry in the table is preserved, so that
- * we don't get a false positive due to the local variable
- * 'string' keeping this entry alive (see job003436).
+ /* Ensure that the first and last entries in the table are
+ * preserved, so that we don't get false positives due to the
+ * local variables 'weak_table' and 'string' keeping these entries
+ * alive (see job003436).
*/
- if (rnd() % 2 == 0 || i + 1 == TABLE_SLOTS) {
+ if (rnd() % 2 == 0 || i == 0 || i + 1 == TABLE_SLOTS) {
string = alloc_string("iamalive", leafap);
preserve[i] = string;
} else {
@@ -216,17 +215,19 @@ static void test(mps_arena_t arena,
}
}
- mps_arena_collect(arena);
+ die(mps_arena_collect(arena), "mps_arena_collect");
mps_arena_release(arena);
for(i = 0; i < TABLE_SLOTS; ++i) {
if (preserve[i] == 0) {
if (table_slot(weaktable, i)) {
- error("Strongly unreachable weak table entry found, slot %lu.\n", i);
+ error("Strongly unreachable weak table entry found, "
+ "slot %"PRIuLONGEST".\n", (ulongest_t)i);
} else {
if (table_slot(exacttable, i) != 0) {
error("Weak table entry deleted, but corresponding "
- "exact table entry not deleted, slot %lu.\n", i);
+ "exact table entry not deleted, slot %"PRIuLONGEST".\n",
+ (ulongest_t)i);
}
}
}
@@ -274,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");
@@ -316,8 +314,7 @@ int main(int argc, char *argv[])
mps_thr_t thread;
void *r;
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
+ testlib_init(argc, argv);
initialise_wrapper(wrapper_wrapper);
initialise_wrapper(string_wrapper);
@@ -339,7 +336,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/awluthe.c b/code/awluthe.c
index 6b56d05f78..6ea468977f 100644
--- a/code/awluthe.c
+++ b/code/awluthe.c
@@ -1,7 +1,7 @@
/* awluthe.c: POOL CLASS AWL UNIT TEST WITH OBJECT HEADERS
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* DESIGN
*
@@ -17,10 +17,9 @@
#include "mpslib.h"
#include "mps.h"
#include "mpstd.h"
-#ifdef MPS_OS_W3
-#include "mpsw3.h"
-#endif
-#include
+
+#include /* strlen */
+#include /* printf */
#define testArenaSIZE ((size_t)64<<20)
@@ -121,7 +120,7 @@ static mps_word_t *alloc_string(const char *s, mps_ap_t ap)
* .assume.dylan-obj
*/
-static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap)
+static mps_word_t *alloc_table(size_t n, mps_ap_t ap)
{
size_t objsize;
void *p;
@@ -130,7 +129,7 @@ static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap)
objsize = (3 + n) * sizeof(mps_word_t);
objsize = size_tAlignUp(objsize, MPS_PF_ALIGN);
do {
- unsigned long i;
+ size_t i;
die(mps_reserve(&p, ap, objsize + headerSIZE), "Reserve Table\n");
object = (mps_word_t *)((char *)p + headerSIZE);
@@ -150,7 +149,7 @@ static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap)
/* gets the nth slot from a table
* .assume.dylan-obj
*/
-static mps_word_t *table_slot(mps_word_t *table, unsigned long n)
+static mps_word_t *table_slot(mps_word_t *table, size_t n)
{
return (mps_word_t *)table[3+n];
}
@@ -159,8 +158,7 @@ static mps_word_t *table_slot(mps_word_t *table, unsigned long n)
/* sets the nth slot in a table
* .assume.dylan-obj
*/
-static void set_table_slot(mps_word_t *table,
- unsigned long n, mps_word_t *p)
+static void set_table_slot(mps_word_t *table, size_t n, mps_word_t *p)
{
cdie(table[0] == (mps_word_t)table_wrapper, "set_table_slot");
table[3+n] = (mps_word_t)p;
@@ -187,7 +185,7 @@ static void test(mps_arena_t arena,
mps_word_t *exacttable;
mps_word_t *preserve[TABLE_SLOTS]; /* preserves objects in the weak */
/* table by referring to them */
- unsigned long i, j;
+ size_t i, j;
void *p;
exacttable = alloc_table(TABLE_SLOTS, exactap);
@@ -221,17 +219,19 @@ static void test(mps_arena_t arena,
}
}
- mps_arena_collect(arena);
+ die(mps_arena_collect(arena), "mps_arena_collect");
mps_arena_release(arena);
for(i = 0; i < TABLE_SLOTS; ++i) {
if (preserve[i] == 0) {
if (table_slot(weaktable, i)) {
- error("Strongly unreachable weak table entry found, slot %lu.\n", i);
+ error("Strongly unreachable weak table entry found, "
+ "slot %"PRIuLONGEST".\n", (ulongest_t)i);
} else {
if (table_slot(exacttable, i) != 0) {
error("Weak table entry deleted, but corresponding "
- "exact table entry not deleted, slot %lu.\n", i);
+ "exact table entry not deleted, slot %"PRIuLONGEST".\n",
+ (ulongest_t)i);
}
}
}
@@ -274,12 +274,9 @@ static void *setup(void *v, size_t s)
die(mps_root_create_reg(&stack, arena, mps_rank_ambig(), 0, thr,
mps_stack_scan_ambig, v, 0),
"Root Create\n");
- EnsureHeaderFormat(&dylanfmt, arena);
- EnsureHeaderWeakFormat(&dylanweakfmt, arena);
+ 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");
@@ -319,8 +316,7 @@ int main(int argc, char *argv[])
mps_thr_t thread;
void *r;
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
+ testlib_init(argc, argv);
initialise_wrapper(wrapper_wrapper);
initialise_wrapper(string_wrapper);
@@ -342,7 +338,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/awlutth.c b/code/awlutth.c
index c937535a0c..2bfaddc381 100644
--- a/code/awlutth.c
+++ b/code/awlutth.c
@@ -1,7 +1,7 @@
/* awlutth.c: THREADING UNIT TEST USING POOL CLASS AWL
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* DESIGN
*
@@ -13,16 +13,13 @@
#include "mpsavm.h"
#include "fmtdy.h"
#include "testlib.h"
+#include "testthr.h"
#include "mpslib.h"
#include "mps.h"
#include "mpstd.h"
-#ifdef MPS_OS_W3
-#include "mpsw3.h"
-#endif
-#include
-#if defined(MPS_OS_LI) || defined(MPS_OS_FR) || defined(MPS_OS_XC)
-#include
-#endif
+
+#include /* printf, puts */
+#include /* strlen */
#define testArenaSIZE ((size_t)64<<20)
@@ -314,10 +311,9 @@ static void *setup_thr(void *v)
int main(int argc, char *argv[])
{
mps_arena_t arena;
- pthread_t pthread1;
+ testthr_t thread1;
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
+ testlib_init(argc, argv);
initialise_wrapper(wrapper_wrapper);
initialise_wrapper(string_wrapper);
@@ -325,9 +321,9 @@ int main(int argc, char *argv[])
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
"arena_create\n");
- pthread_create(&pthread1, NULL, setup_thr, (void *)arena);
+ testthr_create(&thread1, setup_thr, arena);
setup_thr(arena);
- pthread_join(pthread1, NULL);
+ testthr_join(&thread1, NULL);
mps_arena_destroy(arena);
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
@@ -337,7 +333,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/boot.c b/code/boot.c
index f42d5ded7c..af365d3b4f 100644
--- a/code/boot.c
+++ b/code/boot.c
@@ -1,7 +1,7 @@
/* boot.c: BOOTSTRAP ALLOCATOR
*
* $Id$
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .overview: A structure and protocols for allocating memory from a
* given block. Very simple, it basically just increments a pointer.
@@ -30,7 +30,7 @@ Bool BootBlockCheck(BootBlock boot)
CHECKL(boot->limit != NULL);
CHECKL(boot->base <= boot->alloc);
CHECKL(boot->alloc <= boot->limit);
- CHECKL(boot->alloc < boot->limit);
+ CHECKL(boot->base < boot->limit);
return TRUE;
}
@@ -101,7 +101,7 @@ Res BootAlloc(void **pReturn, BootBlock boot, size_t size, size_t align)
AVER(pReturn != NULL);
AVERT(BootBlock, boot);
AVER(size > 0);
- AVER(AlignCheck((Align)align));
+ AVERT(Align, (Align)align);
/* Align alloc pointer up and bounds check. */
blockBase = PointerAlignUp(boot->alloc, align);
@@ -127,7 +127,7 @@ Res BootAlloc(void **pReturn, BootBlock boot, size_t size, size_t align)
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/bt.c b/code/bt.c
index a58e501ee5..1a79b82f5f 100644
--- a/code/bt.c
+++ b/code/bt.c
@@ -1,7 +1,7 @@
/* bt.c: BIT TABLES
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* READERSHIP
*
@@ -10,6 +10,12 @@
* DESIGN
*
* .design: see
+ *
+ * .aver.critical: The function BTIsResRange (and anything it calls)
+ * is on the critical path because it is
+ * called by NailboardIsResRange, which is called for every object in
+ * a nailboarded segment when the segment is scanned or reclaimed; see
+ * .
*/
#include "bt.h"
@@ -90,7 +96,9 @@ SRCID(bt, "$Id$");
} else { \
Index actInnerBase = BTIndexAlignUp((base)); \
if (actInnerBase > (limit)) { /* no inner range */ \
- AVER((base) < (limit)); /* caught by small range case */ \
+ /* Must have base < limit otherwise caught by small range case */ \
+ /* And see .aver.critical. */ \
+ AVER_CRITICAL((base) < (limit)); \
bits_action(BTWordIndex((base)), \
BTBitIndex((base)), \
BTBitIndex((limit))); \
@@ -215,7 +223,7 @@ void BTDestroy(BT bt, Arena arena, Count length)
* discussed in review.impl.c.bt.4.
*/
-static Bool BTCheck(BT bt)
+Bool BTCheck(BT bt)
{
AVER(bt != NULL);
AVER(AddrIsAligned((Addr)bt, sizeof(Word)));
@@ -244,7 +252,7 @@ Size (BTSize)(Count n)
Bool (BTGet)(BT t, Index i)
{
- AVER(BTCheck(t));
+ AVERT(BT, t);
/* Can't check i */
/* see macro in */
@@ -259,7 +267,7 @@ Bool (BTGet)(BT t, Index i)
void (BTSet)(BT t, Index i)
{
- AVER(BTCheck(t));
+ AVERT(BT, t);
/* Can't check i */
/* see macro in */
@@ -274,7 +282,7 @@ void (BTSet)(BT t, Index i)
void (BTRes)(BT t, Index i)
{
- AVER(BTCheck(t));
+ AVERT(BT, t);
/* Can't check i */
/* see macro in */
@@ -289,7 +297,7 @@ void (BTRes)(BT t, Index i)
void BTSetRange(BT t, Index base, Index limit)
{
- AVER(BTCheck(t));
+ AVERT(BT, t);
AVER(base < limit);
#define SINGLE_SET_RANGE(i) \
@@ -311,8 +319,8 @@ void BTSetRange(BT t, Index base, Index limit)
Bool BTIsResRange(BT bt, Index base, Index limit)
{
- AVER(BTCheck(bt));
- AVER(base < limit);
+ AVERT_CRITICAL(BT, bt); /* See .aver.critical */
+ AVER_CRITICAL(base < limit);
/* Can't check range of base or limit */
#define SINGLE_IS_RES_RANGE(i) \
@@ -335,7 +343,7 @@ Bool BTIsResRange(BT bt, Index base, Index limit)
Bool BTIsSetRange(BT bt, Index base, Index limit)
{
- AVER(BTCheck(bt));
+ AVERT(BT, bt);
AVER(base < limit);
/* Can't check range of base or limit */
@@ -363,7 +371,7 @@ Bool BTIsSetRange(BT bt, Index base, Index limit)
void BTResRange(BT t, Index base, Index limit)
{
- AVER(BTCheck(t));
+ AVERT(BT, t);
AVER(base < limit);
#define SINGLE_RES_RANGE(i) \
@@ -876,8 +884,8 @@ Bool BTFindShortResRangeHigh(Index *baseReturn, Index *limitReturn,
Bool BTRangesSame(BT comparand, BT comparator, Index base, Index limit)
{
- AVER(BTCheck(comparand));
- AVER(BTCheck(comparator));
+ AVERT(BT, comparand);
+ AVERT(BT, comparator);
AVER(base < limit);
#define SINGLE_RANGES_SAME(i) \
@@ -912,8 +920,8 @@ Bool BTRangesSame(BT comparand, BT comparator, Index base, Index limit)
void BTCopyInvertRange(BT fromBT, BT toBT, Index base, Index limit)
{
- AVER(BTCheck(fromBT));
- AVER(BTCheck(toBT));
+ AVERT(BT, fromBT);
+ AVERT(BT, toBT);
AVER(fromBT != toBT);
AVER(base < limit);
@@ -947,8 +955,8 @@ void BTCopyInvertRange(BT fromBT, BT toBT, Index base, Index limit)
void BTCopyRange(BT fromBT, BT toBT, Index base, Index limit)
{
- AVER(BTCheck(fromBT));
- AVER(BTCheck(toBT));
+ AVERT(BT, fromBT);
+ AVERT(BT, toBT);
AVER(fromBT != toBT);
AVER(base < limit);
@@ -991,8 +999,8 @@ void BTCopyOffsetRange(BT fromBT, BT toBT,
{
Index fromBit, toBit;
- AVER(BTCheck(fromBT));
- AVER(BTCheck(toBT));
+ AVERT(BT, fromBT);
+ AVERT(BT, toBT);
AVER(fromBT != toBT);
AVER(fromBase < fromLimit);
AVER(toBase < toLimit);
@@ -1016,18 +1024,19 @@ Count BTCountResRange(BT bt, Index base, Index limit)
Count c = 0;
Index bit;
- AVER(BTCheck(bt));
+ AVERT(BT, bt);
AVER(base < limit);
for (bit = base; bit < limit; ++bit)
- if (!BTGet(bt, bit)) ++c;
+ if (!BTGet(bt, bit))
+ ++c;
return c;
}
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/bt.h b/code/bt.h
index 29000b957b..4876f66715 100644
--- a/code/bt.h
+++ b/code/bt.h
@@ -1,7 +1,7 @@
/* bt.h: Bit Table Interface
*
* $Id$
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .source:
*/
@@ -39,6 +39,7 @@ extern void (BTRes)(BT bt, Index index);
END
+extern Bool BTCheck(BT bt);
extern Res BTCreate(BT *btReturn, Arena arena, Count length);
extern void BTDestroy(BT bt, Arena arena, Count length);
@@ -76,7 +77,7 @@ extern Count BTCountResRange(BT bt, Index base, Index limit);
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/btcv.c b/code/btcv.c
index 72256a5f69..33e3df8be2 100644
--- a/code/btcv.c
+++ b/code/btcv.c
@@ -1,7 +1,7 @@
/* btss.c: BIT TABLE COVERAGE TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .readership: MPS developers
*
@@ -18,7 +18,7 @@
#include "testlib.h"
#include "mpslib.h"
-#include
+#include /* printf */
SRCID(btcv, "$Id$");
@@ -550,8 +550,7 @@ int main(int argc, char *argv[])
/* tests need 4 whole words plus a few extra bits */
btSize = MPS_WORD_WIDTH * 4 + 10;
- testlib_unused(argc);
- testlib_unused(argv);
+ testlib_init(argc, argv);
die(mps_arena_create(&mpsArena, mps_arena_class_vm(), testArenaSIZE),
"mps_arena_create");
@@ -572,7 +571,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/bttest.c b/code/bttest.c
index ea2b00849b..20bb4cab4e 100644
--- a/code/bttest.c
+++ b/code/bttest.c
@@ -1,7 +1,7 @@
/* bttest.c: BIT TABLE TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*/
@@ -10,11 +10,10 @@
#include "mpsavm.h"
#include "testlib.h"
#include "mpslib.h"
-
-#include
-#include
#include "mpstd.h"
-#include
+
+#include /* fflush, fgets, printf, putchar, puts */
+#include /* exit, strtol */
SRCID(bttest, "$Id$");
@@ -125,7 +124,7 @@ static void get(void)
{
if (argInRange(0)) {
Bool b = (BTGet)(bt, args[0]);
- printf(b ? "TRUE\n" : "FALSE\n");
+ puts(b ? "TRUE" : "FALSE");
}
}
@@ -148,7 +147,7 @@ static void isSetRange(void)
{
if (checkDefaultRange(0)) {
Bool b = BTIsSetRange(bt, args[0], args[1]);
- printf(b ? "TRUE\n" : "FALSE\n");
+ puts(b ? "TRUE" : "FALSE");
}
}
@@ -157,7 +156,7 @@ static void isResRange(void)
{
if (checkDefaultRange(0)) {
Bool b = BTIsResRange(bt, args[0], args[1]);
- printf(b ? "TRUE\n" : "FALSE\n");
+ puts(b ? "TRUE" : "FALSE");
}
}
@@ -312,11 +311,6 @@ static void obeyCommand(const char *command)
}
-#ifdef MPS_BUILD_MV
-/* disable "conversion from int to char" */
-#pragma warning(disable: 4244)
-#endif
-
static void showBT(void) {
Index i;
char c;
@@ -325,7 +319,7 @@ static void showBT(void) {
i = 0;
while((i < btSize) && (i < 50)) {
if (i % 10 == 0)
- c = (char)((i / 10) % 10) + '0';
+ c = (char)(((i / 10) % 10) + '0');
else
c = ' ';
putchar(c);
@@ -334,7 +328,7 @@ static void showBT(void) {
putchar('\n');
i = 0;
while((i < btSize) && (i < 50)) {
- c = (char)(i % 10) +'0';
+ c = (char)((i % 10) +'0');
putchar(c);
++ i;
}
@@ -353,11 +347,6 @@ static void showBT(void) {
putchar('\n');
}
-#ifdef MPS_BUILD_MV
-/* disable "conversion from int to char" */
-#pragma warning(default: 4244)
-#endif
-
#define testArenaSIZE (((size_t)64)<<20)
@@ -366,7 +355,7 @@ extern int main(int argc, char *argv[])
bt = NULL;
btSize = 0;
- testlib_unused(argc); testlib_unused(argv);
+ testlib_init(argc, argv);
die(mps_arena_create((mps_arena_t*)&arena, mps_arena_class_vm(),
testArenaSIZE),
@@ -374,7 +363,7 @@ extern int main(int argc, char *argv[])
while(1) {
char input[100];
printf("bt test> ");
- fflush(stdout);
+ (void)fflush(stdout);
if (fgets(input, 100, stdin)) {
obeyCommand(input);
showBT();
@@ -387,7 +376,7 @@ extern int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/buffer.c b/code/buffer.c
index c63310980e..edef611c21 100644
--- a/code/buffer.c
+++ b/code/buffer.c
@@ -1,7 +1,7 @@
/* buffer.c: ALLOCATION BUFFER IMPLEMENTATION
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .purpose: This is (part of) the implementation of allocation buffers.
* Several macros which also form part of the implementation are in
@@ -19,10 +19,8 @@
*
* TRANSGRESSIONS
*
- * .trans.mod: There are several instances where pool structures are
- * directly accessed by this module because does not provide
- * an adequate (or adequately documented) interface. They bear this
- * tag.
+ * .trans.mod: pool->bufferSerial is directly accessed by this module
+ * because does not provide an interface.
*/
#include "mpm.h"
@@ -45,7 +43,7 @@ Bool BufferCheck(Buffer buffer)
CHECKU(Arena, buffer->arena);
CHECKU(Pool, buffer->pool);
CHECKL(buffer->arena == buffer->pool->arena);
- CHECKL(RingCheck(&buffer->poolRing)); /* */
+ CHECKD_NOSIG(Ring, &buffer->poolRing);
CHECKL(BoolCheck(buffer->isMutator));
CHECKL(buffer->fillSize >= 0.0);
CHECKL(buffer->emptySize >= 0.0);
@@ -146,47 +144,48 @@ Bool BufferCheck(Buffer buffer)
*
* See for structure definitions. */
-Res BufferDescribe(Buffer buffer, mps_lib_FILE *stream)
+Res BufferDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth)
{
Res res;
- char abzMode[5];
- if (!TESTT(Buffer, buffer)) return ResFAIL;
- if (stream == NULL) return ResFAIL;
-
- abzMode[0] = (char)( (buffer->mode & BufferModeTRANSITION) ? 't' : '_' );
- abzMode[1] = (char)( (buffer->mode & BufferModeLOGGED) ? 'l' : '_' );
- abzMode[2] = (char)( (buffer->mode & BufferModeFLIPPED) ? 'f' : '_' );
- abzMode[3] = (char)( (buffer->mode & BufferModeATTACHED) ? 'a' : '_' );
- abzMode[4] = '\0';
+ if (!TESTT(Buffer, buffer))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
- res = WriteF(stream,
+ res = WriteF(stream, depth,
"Buffer $P ($U) {\n",
(WriteFP)buffer, (WriteFU)buffer->serial,
" class $P (\"$S\")\n",
- (WriteFP)buffer->class, buffer->class->name,
+ (WriteFP)buffer->class, (WriteFS)buffer->class->name,
" Arena $P\n", (WriteFP)buffer->arena,
" Pool $P\n", (WriteFP)buffer->pool,
- buffer->isMutator ?
- " Mutator Buffer\n" : " Internal Buffer\n",
- " mode $S (TRANSITION, LOGGED, FLIPPED, ATTACHED)\n",
- (WriteFS)abzMode,
+ " ", buffer->isMutator ? "Mutator" : "Internal", " Buffer\n",
+ " mode $C$C$C$C (TRANSITION, LOGGED, FLIPPED, ATTACHED)\n",
+ (WriteFC)((buffer->mode & BufferModeTRANSITION) ? 't' : '_'),
+ (WriteFC)((buffer->mode & BufferModeLOGGED) ? 'l' : '_'),
+ (WriteFC)((buffer->mode & BufferModeFLIPPED) ? 'f' : '_'),
+ (WriteFC)((buffer->mode & BufferModeATTACHED) ? 'a' : '_'),
" fillSize $UKb\n", (WriteFU)(buffer->fillSize / 1024),
" emptySize $UKb\n", (WriteFU)(buffer->emptySize / 1024),
" alignment $W\n", (WriteFW)buffer->alignment,
- " base $A\n", buffer->base,
- " initAtFlip $A\n", buffer->initAtFlip,
- " init $A\n", buffer->ap_s.init,
- " alloc $A\n", buffer->ap_s.alloc,
- " limit $A\n", buffer->ap_s.limit,
- " poolLimit $A\n", buffer->poolLimit,
+ " base $A\n", (WriteFA)buffer->base,
+ " initAtFlip $A\n", (WriteFA)buffer->initAtFlip,
+ " init $A\n", (WriteFA)buffer->ap_s.init,
+ " alloc $A\n", (WriteFA)buffer->ap_s.alloc,
+ " limit $A\n", (WriteFA)buffer->ap_s.limit,
+ " poolLimit $A\n", (WriteFA)buffer->poolLimit,
+ " alignment $W\n", (WriteFW)buffer->alignment,
+ " rampCount $U\n", (WriteFU)buffer->rampCount,
NULL);
- if (res != ResOK) return res;
+ if (res != ResOK)
+ return res;
- res = buffer->class->describe(buffer, stream);
- if (res != ResOK) return res;
+ res = buffer->class->describe(buffer, stream, depth + 2);
+ if (res != ResOK)
+ return res;
- res = WriteF(stream, "} Buffer $P ($U)\n",
+ res = WriteF(stream, depth, "} Buffer $P ($U)\n",
(WriteFP)buffer, (WriteFU)buffer->serial,
NULL);
return res;
@@ -204,8 +203,6 @@ static Res BufferInit(Buffer buffer, BufferClass class,
AVER(buffer != NULL);
AVERT(BufferClass, class);
AVERT(Pool, pool);
- /* The PoolClass should support buffer protocols */
- AVER((pool->class->attr & AttrBUF)); /* .trans.mod */
arena = PoolArena(pool);
/* Initialize the buffer. See for a definition of */
@@ -222,7 +219,7 @@ static Res BufferInit(Buffer buffer, BufferClass class,
}
buffer->fillSize = 0.0;
buffer->emptySize = 0.0;
- buffer->alignment = pool->alignment; /* .trans.mod */
+ buffer->alignment = PoolAlignment(pool);
buffer->base = (Addr)0;
buffer->initAtFlip = (Addr)0;
/* In the next three assignments we really mean zero, not NULL, because
@@ -329,12 +326,10 @@ void BufferDetach(Buffer buffer, Pool pool)
spare = AddrOffset(init, limit);
buffer->emptySize += spare;
if (buffer->isMutator) {
- buffer->pool->emptyMutatorSize += spare;
ArenaGlobals(buffer->arena)->emptyMutatorSize += spare;
ArenaGlobals(buffer->arena)->allocMutatorSize +=
AddrOffset(buffer->base, init);
} else {
- buffer->pool->emptyInternalSize += spare;
ArenaGlobals(buffer->arena)->emptyInternalSize += spare;
}
@@ -382,8 +377,6 @@ void BufferFinish(Buffer buffer)
pool = BufferPool(buffer);
- /* The PoolClass should support buffer protocols */
- AVER((pool->class->attr & AttrBUF)); /* .trans.mod */
AVER(BufferIsReady(buffer));
/* */
@@ -605,7 +598,7 @@ Res BufferReserve(Addr *pReturn, Buffer buffer, Size size,
AVER(size > 0);
AVER(SizeIsAligned(size, BufferPool(buffer)->alignment));
AVER(BufferIsReady(buffer));
- AVER(BoolCheck(withReservoirPermit));
+ AVERT(Bool, withReservoirPermit);
/* Is there enough room in the unallocated portion of the buffer to */
/* satisfy the request? If so, just increase the alloc marker and */
@@ -660,10 +653,8 @@ void BufferAttach(Buffer buffer, Addr base, Addr limit,
Size prealloc = AddrOffset(base, init);
ArenaGlobals(buffer->arena)->allocMutatorSize -= prealloc;
}
- buffer->pool->fillMutatorSize += filled;
ArenaGlobals(buffer->arena)->fillMutatorSize += filled;
} else {
- buffer->pool->fillInternalSize += filled;
ArenaGlobals(buffer->arena)->fillInternalSize += filled;
}
@@ -980,20 +971,21 @@ Bool BufferIsTrappedByMutator(Buffer buffer)
*
* Just represent the two patterns by two different pointers to dummies. */
-AllocPatternStruct AllocPatternRampStruct = {'\0'};
+static AllocPatternStruct AllocPatternRampStruct = {'\0'};
AllocPattern AllocPatternRamp(void)
{
return &AllocPatternRampStruct;
}
-AllocPatternStruct AllocPatternRampCollectAllStruct = {'\0'};
+static AllocPatternStruct AllocPatternRampCollectAllStruct = {'\0'};
AllocPattern AllocPatternRampCollectAll(void)
{
return &AllocPatternRampCollectAllStruct;
}
+ATTRIBUTE_UNUSED
static Bool AllocPatternCheck(AllocPattern pattern)
{
CHECKL(pattern == &AllocPatternRampCollectAllStruct
@@ -1075,7 +1067,7 @@ static Res bufferTrivInit(Buffer buffer, Pool pool, ArgList args)
AVERT(Buffer, buffer);
AVERT(Pool, pool);
UNUSED(args);
- EVENT3(BufferInit, buffer, pool, buffer->isMutator);
+ EVENT3(BufferInit, buffer, pool, BOOLOF(buffer->isMutator));
return ResOK;
}
@@ -1169,10 +1161,13 @@ static void bufferNoReassignSeg(Buffer buffer, Seg seg)
/* bufferTrivDescribe -- basic Buffer describe method */
-static Res bufferTrivDescribe(Buffer buffer, mps_lib_FILE *stream)
+static Res bufferTrivDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth)
{
- if (!TESTT(Buffer, buffer)) return ResFAIL;
- if (stream == NULL) return ResFAIL;
+ if (!TESTT(Buffer, buffer))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
+ UNUSED(depth);
/* dispatching function does it all */
return ResOK;
}
@@ -1182,7 +1177,7 @@ static Res bufferTrivDescribe(Buffer buffer, mps_lib_FILE *stream)
Bool BufferClassCheck(BufferClass class)
{
- CHECKL(ProtocolClassCheck(&class->protocol));
+ CHECKD(ProtocolClass, &class->protocol);
CHECKL(class->name != NULL); /* Should be <=6 char C identifier */
CHECKL(class->size >= sizeof(BufferStruct));
CHECKL(FUNCHECK(class->varargs));
@@ -1220,6 +1215,7 @@ DEFINE_CLASS(BufferClass, class)
class->setRankSet = bufferNoSetRankSet;
class->reassignSeg = bufferNoReassignSeg;
class->sig = BufferClassSig;
+ AVERT(BufferClass, class);
}
@@ -1240,7 +1236,7 @@ Bool SegBufCheck(SegBuf segbuf)
CHECKS(SegBuf, segbuf);
buffer = &segbuf->bufferStruct;
- CHECKL(BufferCheck(buffer));
+ CHECKD(Buffer, buffer);
CHECKL(RankSetCheck(segbuf->rankSet));
if (buffer->mode & BufferModeTRANSITION) {
@@ -1250,7 +1246,7 @@ Bool SegBufCheck(SegBuf segbuf)
} else {
/* The buffer is attached to a segment. */
CHECKL(segbuf->seg != NULL);
- CHECKL(SegCheck(segbuf->seg));
+ CHECKD(Seg, segbuf->seg);
/* To avoid recursive checking, leave it to SegCheck to make */
/* sure the buffer and segment fields tally. */
@@ -1287,7 +1283,7 @@ static Res segBufInit(Buffer buffer, Pool pool, ArgList args)
segbuf->rankSet = RankSetEMPTY;
AVERT(SegBuf, segbuf);
- EVENT3(BufferInitSeg, buffer, pool, buffer->isMutator);
+ EVENT3(BufferInitSeg, buffer, pool, BOOLOF(buffer->isMutator));
return ResOK;
}
@@ -1426,25 +1422,29 @@ static void segBufReassignSeg (Buffer buffer, Seg seg)
/* segBufDescribe -- describe method for SegBuf */
-static Res segBufDescribe(Buffer buffer, mps_lib_FILE *stream)
+static Res segBufDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth)
{
SegBuf segbuf;
BufferClass super;
Res res;
- if (!TESTT(Buffer, buffer)) return ResFAIL;
- if (stream == NULL) return ResFAIL;
+ if (!TESTT(Buffer, buffer))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
segbuf = BufferSegBuf(buffer);
- if (!TESTT(SegBuf, segbuf)) return ResFAIL;
+ if (!TESTT(SegBuf, segbuf))
+ return ResFAIL;
/* Describe the superclass fields first via next-method call */
super = BUFFER_SUPERCLASS(SegBufClass);
- res = super->describe(buffer, stream);
- if (res != ResOK) return res;
+ res = super->describe(buffer, stream, depth);
+ if (res != ResOK)
+ return res;
- res = WriteF(stream,
- " Seg $P\n", (WriteFP)segbuf->seg,
- " rankSet $U\n", (WriteFU)segbuf->rankSet,
+ res = WriteF(stream, depth,
+ "Seg $P\n", (WriteFP)segbuf->seg,
+ "rankSet $U\n", (WriteFU)segbuf->rankSet,
NULL);
return res;
@@ -1472,6 +1472,7 @@ DEFINE_CLASS(SegBufClass, class)
class->rankSet = segBufRankSet;
class->setRankSet = segBufSetRankSet;
class->reassignSeg = segBufReassignSeg;
+ AVERT(BufferClass, class);
}
@@ -1485,7 +1486,7 @@ static void rankBufVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs)
args[0].key = MPS_KEY_RANK;
args[0].val.rank = va_arg(varargs, Rank);
args[1].key = MPS_KEY_ARGS_END;
- AVER(ArgListCheck(args));
+ AVERT(ArgList, args);
}
/* rankBufInit -- RankBufClass init method */
@@ -1499,10 +1500,10 @@ static Res rankBufInit(Buffer buffer, Pool pool, ArgList args)
AVERT(Buffer, buffer);
AVERT(Pool, pool);
- AVER(ArgListCheck(args));
+ AVERT(ArgList, args);
if (ArgPick(&arg, args, MPS_KEY_RANK))
rank = arg.val.rank;
- AVER(RankCheck(rank));
+ AVERT(Rank, rank);
/* Initialize the superclass fields first via next-method call */
super = BUFFER_SUPERCLASS(RankBufClass);
@@ -1513,7 +1514,7 @@ static Res rankBufInit(Buffer buffer, Pool pool, ArgList args)
BufferSetRankSet(buffer, RankSetSingle(rank));
/* There's nothing to check that the superclass doesn't, so no AVERT. */
- EVENT4(BufferInitRank, buffer, pool, buffer->isMutator, rank);
+ EVENT4(BufferInitRank, buffer, pool, BOOLOF(buffer->isMutator), rank);
return ResOK;
}
@@ -1532,12 +1533,13 @@ DEFINE_CLASS(RankBufClass, class)
class->name = "RANKBUF";
class->varargs = rankBufVarargs;
class->init = rankBufInit;
+ AVERT(BufferClass, class);
}
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/cbs.c b/code/cbs.c
index 84913dd9ea..0b30b68acb 100644
--- a/code/cbs.c
+++ b/code/cbs.c
@@ -1,7 +1,7 @@
/* cbs.c: COALESCING BLOCK STRUCTURE IMPLEMENTATION
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license.
*
* .intro: This is a portable implementation of coalescing block
* structures.
@@ -21,49 +21,29 @@
SRCID(cbs, "$Id$");
-typedef struct CBSBlockStruct *CBSBlock;
-typedef struct CBSBlockStruct {
- SplayNodeStruct splayNode;
- Addr base;
- Addr limit;
- Size maxSize; /* accurate maximum block size of sub-tree */
-} CBSBlockStruct;
-
#define CBSBlockBase(block) ((block)->base)
#define CBSBlockLimit(block) ((block)->limit)
#define CBSBlockSize(block) AddrOffset((block)->base, (block)->limit)
-#define cbsOfSplayTree(tree) PARENT(CBSStruct, splayTree, (tree))
-#define cbsBlockOfSplayNode(node) PARENT(CBSBlockStruct, splayNode, (node))
-#define splayTreeOfCBS(tree) (&((cbs)->splayTree))
-#define splayNodeOfCBSBlock(block) (&((block)->splayNode))
-#define keyOfCBSBlock(block) ((void *)&((block)->base))
-
-
-/* cbsEnter, cbsLeave -- Avoid re-entrance
- *
- * .enter-leave: The callbacks are restricted in what they may call.
- * These functions enforce this.
- *
- * .enter-leave.simple: Simple queries may be called from callbacks.
- */
-
-static void cbsEnter(CBS cbs)
-{
- /* Don't need to check as always called from interface function. */
- AVER(!cbs->inCBS);
- cbs->inCBS = TRUE;
- return;
-}
+#define cbsOfLand(land) PARENT(CBSStruct, landStruct, land)
+#define cbsSplay(cbs) (&((cbs)->splayTreeStruct))
+#define cbsOfSplay(_splay) PARENT(CBSStruct, splayTreeStruct, _splay)
+#define cbsBlockTree(block) (&((block)->treeStruct))
+#define cbsBlockOfTree(_tree) TREE_ELT(CBSBlock, treeStruct, _tree)
+#define cbsFastBlockOfTree(_tree) \
+ PARENT(CBSFastBlockStruct, cbsBlockStruct, cbsBlockOfTree(_tree))
+#define cbsZonedBlockOfTree(_tree) \
+ PARENT(CBSZonedBlockStruct, cbsFastBlockStruct, cbsFastBlockOfTree(_tree))
+#define cbsBlockPool(cbs) RVALUE((cbs)->blockPool)
-static void cbsLeave(CBS cbs)
-{
- /* Don't need to check as always called from interface function. */
- AVER(cbs->inCBS);
- cbs->inCBS = FALSE;
- return;
-}
+/* We pass the block base directly as a TreeKey (void *) assuming that
+ Addr can be encoded, and possibly breaking .
+ On an exotic platform where this isn't true, pass the address of base.
+ i.e. add an & */
+#define cbsBlockKey(block) ((TreeKey)(block)->base)
+#define keyOfBaseVar(baseVar) ((TreeKey)(baseVar))
+#define baseOfKey(key) ((Addr)(key))
/* CBSCheck -- Check CBS */
@@ -71,25 +51,28 @@ static void cbsLeave(CBS cbs)
Bool CBSCheck(CBS cbs)
{
/* See .enter-leave.simple. */
+ Land land;
CHECKS(CBS, cbs);
- CHECKL(cbs != NULL);
- CHECKL(SplayTreeCheck(splayTreeOfCBS(cbs)));
- /* nothing to check about splayTreeSize */
+ land = CBSLand(cbs);
+ CHECKD(Land, land);
+ CHECKD(SplayTree, cbsSplay(cbs));
CHECKD(Pool, cbs->blockPool);
- CHECKL(BoolCheck(cbs->fastFind));
- CHECKL(BoolCheck(cbs->inCBS));
- /* No MeterCheck */
+ CHECKL(cbs->blockStructSize > 0);
+ CHECKL(BoolCheck(cbs->ownPool));
+ CHECKL(SizeIsAligned(cbs->size, LandAlignment(land)));
+ STATISTIC_STAT({CHECKL((cbs->size == 0) == (cbs->treeSize == 0));});
return TRUE;
}
+ATTRIBUTE_UNUSED
static Bool CBSBlockCheck(CBSBlock block)
{
- /* See .enter-leave.simple. */
UNUSED(block); /* Required because there is no signature */
CHECKL(block != NULL);
- CHECKL(SplayNodeCheck(splayNodeOfCBSBlock(block)));
+ /* Can't use CHECKD_NOSIG because TreeEMPTY is NULL. */
+ CHECKL(TreeCheck(cbsBlockTree(block)));
/* If the block is in the middle of being deleted, */
/* the pointers will be equal. */
@@ -99,24 +82,22 @@ static Bool CBSBlockCheck(CBSBlock block)
}
-/* cbsSplayCompare -- Compare key to [base,limit)
+/* cbsCompare -- Compare key to [base,limit)
*
* See
*/
-static Compare cbsSplayCompare(void *key, SplayNode node)
+static Compare cbsCompare(Tree tree, TreeKey key)
{
Addr base1, base2, limit2;
CBSBlock cbsBlock;
- /* NULL key compares less than everything. */
- if (key == NULL)
- return CompareLESS;
-
- AVER(node != NULL);
+ AVERT_CRITICAL(Tree, tree);
+ AVER_CRITICAL(tree != TreeEMPTY);
+ AVER_CRITICAL(key != NULL);
- base1 = *(Addr *)key;
- cbsBlock = cbsBlockOfSplayNode(node);
+ base1 = baseOfKey(key);
+ cbsBlock = cbsBlockOfTree(tree);
base2 = cbsBlock->base;
limit2 = cbsBlock->limit;
@@ -128,167 +109,254 @@ static Compare cbsSplayCompare(void *key, SplayNode node)
return CompareEQUAL;
}
+static TreeKey cbsKey(Tree tree)
+{
+ return cbsBlockKey(cbsBlockOfTree(tree));
+}
+
/* cbsTestNode, cbsTestTree -- test for nodes larger than the S parameter */
-static Bool cbsTestNode(SplayTree tree, SplayNode node,
+static Bool cbsTestNode(SplayTree splay, Tree tree,
void *closureP, Size size)
{
CBSBlock block;
- AVERT(SplayTree, tree);
- AVERT(SplayNode, node);
+ AVERT(SplayTree, splay);
+ AVERT(Tree, tree);
AVER(closureP == NULL);
AVER(size > 0);
- AVER(cbsOfSplayTree(tree)->fastFind);
+ AVER(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSFastLandClass));
- block = cbsBlockOfSplayNode(node);
+ block = cbsBlockOfTree(tree);
return CBSBlockSize(block) >= size;
}
-static Bool cbsTestTree(SplayTree tree, SplayNode node,
+static Bool cbsTestTree(SplayTree splay, Tree tree,
void *closureP, Size size)
{
- CBSBlock block;
+ CBSFastBlock block;
- AVERT(SplayTree, tree);
- AVERT(SplayNode, node);
+ AVERT(SplayTree, splay);
+ AVERT(Tree, tree);
AVER(closureP == NULL);
AVER(size > 0);
- AVER(cbsOfSplayTree(tree)->fastFind);
+ AVER(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSFastLandClass));
- block = cbsBlockOfSplayNode(node);
+ block = cbsFastBlockOfTree(tree);
return block->maxSize >= size;
}
-/* cbsUpdateNode -- update size info after restructuring */
+/* cbsUpdateFastNode -- update size info after restructuring */
-static void cbsUpdateNode(SplayTree tree, SplayNode node,
- SplayNode leftChild, SplayNode rightChild)
+static void cbsUpdateFastNode(SplayTree splay, Tree tree)
{
Size maxSize;
- CBSBlock block;
- AVERT(SplayTree, tree);
- AVERT(SplayNode, node);
- if (leftChild != NULL)
- AVERT(SplayNode, leftChild);
- if (rightChild != NULL)
- AVERT(SplayNode, rightChild);
- AVER(cbsOfSplayTree(tree)->fastFind);
+ AVERT_CRITICAL(SplayTree, splay);
+ AVERT_CRITICAL(Tree, tree);
+ AVER_CRITICAL(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSFastLandClass));
- block = cbsBlockOfSplayNode(node);
- maxSize = CBSBlockSize(block);
+ maxSize = CBSBlockSize(cbsBlockOfTree(tree));
- if (leftChild != NULL) {
- Size size = cbsBlockOfSplayNode(leftChild)->maxSize;
+ if (TreeHasLeft(tree)) {
+ Size size = cbsFastBlockOfTree(TreeLeft(tree))->maxSize;
if (size > maxSize)
maxSize = size;
}
- if (rightChild != NULL) {
- Size size = cbsBlockOfSplayNode(rightChild)->maxSize;
+ if (TreeHasRight(tree)) {
+ Size size = cbsFastBlockOfTree(TreeRight(tree))->maxSize;
if (size > maxSize)
maxSize = size;
}
- block->maxSize = maxSize;
+ cbsFastBlockOfTree(tree)->maxSize = maxSize;
}
-/* CBSInit -- Initialise a CBS structure
+/* cbsUpdateZonedNode -- update size and zone info after restructuring */
+
+static void cbsUpdateZonedNode(SplayTree splay, Tree tree)
+{
+ ZoneSet zones;
+ CBSZonedBlock zonedBlock;
+ CBSBlock block;
+ Arena arena;
+
+ AVERT_CRITICAL(SplayTree, splay);
+ AVERT_CRITICAL(Tree, tree);
+ AVER_CRITICAL(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSZonedLandClass));
+
+ cbsUpdateFastNode(splay, tree);
+
+ zonedBlock = cbsZonedBlockOfTree(tree);
+ block = &zonedBlock->cbsFastBlockStruct.cbsBlockStruct;
+ arena = LandArena(CBSLand(cbsOfSplay(splay)));
+ zones = ZoneSetOfRange(arena, CBSBlockBase(block), CBSBlockLimit(block));
+
+ if (TreeHasLeft(tree))
+ zones = ZoneSetUnion(zones, cbsZonedBlockOfTree(TreeLeft(tree))->zones);
+
+ if (TreeHasRight(tree))
+ zones = ZoneSetUnion(zones, cbsZonedBlockOfTree(TreeRight(tree))->zones);
+
+ zonedBlock->zones = zones;
+}
+
+
+/* cbsInit -- Initialise a CBS structure
*
- * See .
+ * See .
*/
-ARG_DEFINE_KEY(cbs_extend_by, Size);
+ARG_DEFINE_KEY(cbs_block_pool, Pool);
-Res CBSInit(Arena arena, CBS cbs, void *owner, Align alignment,
- Bool fastFind, ArgList args)
+static Res cbsInitComm(Land land, ArgList args, SplayUpdateNodeFunction update,
+ Size blockStructSize)
{
- Size extendBy = CBS_EXTEND_BY_DEFAULT;
+ CBS cbs;
+ LandClass super;
ArgStruct arg;
Res res;
+ Pool blockPool = NULL;
- AVERT(Arena, arena);
-
- if (ArgPick(&arg, args, MPS_KEY_CBS_EXTEND_BY))
- extendBy = arg.val.size;
-
- SplayTreeInit(splayTreeOfCBS(cbs), &cbsSplayCompare,
- fastFind ? &cbsUpdateNode : NULL);
- MPS_ARGS_BEGIN(pcArgs) {
- MPS_ARGS_ADD(pcArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSBlockStruct));
- MPS_ARGS_ADD(pcArgs, MPS_KEY_EXTEND_BY, extendBy);
- res = PoolCreate(&(cbs->blockPool), arena, PoolClassMFS(), pcArgs);
- } MPS_ARGS_END(pcArgs);
+ AVERT(Land, land);
+ super = LAND_SUPERCLASS(CBSLandClass);
+ res = (*super->init)(land, args);
if (res != ResOK)
return res;
- cbs->splayTreeSize = 0;
- cbs->fastFind = fastFind;
- cbs->alignment = alignment;
- cbs->inCBS = TRUE;
+ if (ArgPick(&arg, args, CBSBlockPool))
+ blockPool = arg.val.pool;
- METER_INIT(cbs->splaySearch, "size of splay tree", (void *)cbs);
+ cbs = cbsOfLand(land);
+ SplayTreeInit(cbsSplay(cbs), cbsCompare, cbsKey, update);
+
+ if (blockPool != NULL) {
+ cbs->blockPool = blockPool;
+ cbs->ownPool = FALSE;
+ } else {
+ MPS_ARGS_BEGIN(pcArgs) {
+ MPS_ARGS_ADD(pcArgs, MPS_KEY_MFS_UNIT_SIZE, blockStructSize);
+ res = PoolCreate(&cbs->blockPool, LandArena(land), PoolClassMFS(), pcArgs);
+ } MPS_ARGS_END(pcArgs);
+ if (res != ResOK)
+ return res;
+ cbs->ownPool = TRUE;
+ }
+ cbs->treeSize = 0;
+ cbs->size = 0;
+
+ cbs->blockStructSize = blockStructSize;
+
+ METER_INIT(cbs->treeSearch, "size of tree", (void *)cbs);
cbs->sig = CBSSig;
AVERT(CBS, cbs);
- EVENT2(CBSInit, cbs, owner);
- cbsLeave(cbs);
return ResOK;
}
+static Res cbsInit(Land land, ArgList args)
+{
+ return cbsInitComm(land, args, SplayTrivUpdate,
+ sizeof(CBSBlockStruct));
+}
+
+static Res cbsInitFast(Land land, ArgList args)
+{
+ return cbsInitComm(land, args, cbsUpdateFastNode,
+ sizeof(CBSFastBlockStruct));
+}
+
+static Res cbsInitZoned(Land land, ArgList args)
+{
+ return cbsInitComm(land, args, cbsUpdateZonedNode,
+ sizeof(CBSZonedBlockStruct));
+}
-/* CBSFinish -- Finish a CBS structure
+
+/* cbsFinish -- Finish a CBS structure
*
- * See .
+ * See .
*/
-void CBSFinish(CBS cbs)
+static void cbsFinish(Land land)
{
+ CBS cbs;
+
+ AVERT(Land, land);
+ cbs = cbsOfLand(land);
AVERT(CBS, cbs);
- cbsEnter(cbs);
- METER_EMIT(&cbs->splaySearch);
+ METER_EMIT(&cbs->treeSearch);
cbs->sig = SigInvalid;
- SplayTreeFinish(splayTreeOfCBS(cbs));
- PoolDestroy(cbs->blockPool);
+ SplayTreeFinish(cbsSplay(cbs));
+ if (cbs->ownPool)
+ PoolDestroy(cbsBlockPool(cbs));
}
-/* Node change operators
+/* cbsSize -- total size of ranges in CBS
*
- * These four functions are called whenever blocks are created,
- * destroyed, grow, or shrink. They maintain the maxSize if fastFind is
- * enabled.
+ * See .
*/
-static void cbsBlockDelete(CBS cbs, CBSBlock block)
+static Size cbsSize(Land land)
{
- Res res;
+ CBS cbs;
+
+ AVERT(Land, land);
+ cbs = cbsOfLand(land);
+ AVERT(CBS, cbs);
+
+ return cbs->size;
+}
+
+/* cbsBlockDestroy -- destroy a block */
+
+static void cbsBlockDestroy(CBS cbs, CBSBlock block)
+{
+ Size size;
AVERT(CBS, cbs);
AVERT(CBSBlock, block);
+ size = CBSBlockSize(block);
- METER_ACC(cbs->splaySearch, cbs->splayTreeSize);
- res = SplayTreeDelete(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block),
- keyOfCBSBlock(block));
- AVER(res == ResOK); /* Must be possible to delete node */
- STATISTIC(--cbs->splayTreeSize);
+ STATISTIC(--cbs->treeSize);
+ AVER(cbs->size >= size);
+ cbs->size -= size;
/* make invalid */
block->limit = block->base;
+ PoolFree(cbsBlockPool(cbs), (Addr)block, cbs->blockStructSize);
+}
- PoolFree(cbs->blockPool, (Addr)block, sizeof(CBSBlockStruct));
- return;
+/* Node change operators
+ *
+ * These four functions are called whenever blocks are created,
+ * destroyed, grow, or shrink. They maintain the maxSize if fastFind is
+ * enabled.
+ */
+
+static void cbsBlockDelete(CBS cbs, CBSBlock block)
+{
+ Bool b;
+
+ AVERT(CBS, cbs);
+ AVERT(CBSBlock, block);
+
+ METER_ACC(cbs->treeSearch, cbs->treeSize);
+ b = SplayTreeDelete(cbsSplay(cbs), cbsBlockTree(block));
+ AVER(b); /* expect block to be in the tree */
+ cbsBlockDestroy(cbs, block);
}
static void cbsBlockShrunk(CBS cbs, CBSBlock block, Size oldSize)
@@ -300,12 +368,10 @@ static void cbsBlockShrunk(CBS cbs, CBSBlock block, Size oldSize)
newSize = CBSBlockSize(block);
AVER(oldSize > newSize);
+ AVER(cbs->size >= oldSize - newSize);
- if (cbs->fastFind) {
- SplayNodeRefresh(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block),
- keyOfCBSBlock(block));
- AVER(CBSBlockSize(block) <= block->maxSize);
- }
+ SplayNodeRefresh(cbsSplay(cbs), cbsBlockTree(block));
+ cbs->size -= oldSize - newSize;
}
static void cbsBlockGrew(CBS cbs, CBSBlock block, Size oldSize)
@@ -318,15 +384,12 @@ static void cbsBlockGrew(CBS cbs, CBSBlock block, Size oldSize)
newSize = CBSBlockSize(block);
AVER(oldSize < newSize);
- if (cbs->fastFind) {
- SplayNodeRefresh(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block),
- keyOfCBSBlock(block));
- AVER(CBSBlockSize(block) <= block->maxSize);
- }
+ SplayNodeRefresh(cbsSplay(cbs), cbsBlockTree(block));
+ cbs->size += newSize - oldSize;
}
/* cbsBlockAlloc -- allocate a new block and set its base and limit,
- but do not insert it into the splay tree yet */
+ but do not insert it into the tree yet */
static Res cbsBlockAlloc(CBSBlock *blockReturn, CBS cbs, Range range)
{
@@ -338,16 +401,17 @@ static Res cbsBlockAlloc(CBSBlock *blockReturn, CBS cbs, Range range)
AVERT(CBS, cbs);
AVERT(Range, range);
- res = PoolAlloc(&p, cbs->blockPool, sizeof(CBSBlockStruct),
+ res = PoolAlloc(&p, cbsBlockPool(cbs), cbs->blockStructSize,
/* withReservoirPermit */ FALSE);
if (res != ResOK)
goto failPoolAlloc;
block = (CBSBlock)p;
- SplayNodeInit(splayNodeOfCBSBlock(block));
+ TreeInit(cbsBlockTree(block));
block->base = RangeBase(range);
block->limit = RangeLimit(range);
- block->maxSize = CBSBlockSize(block);
+
+ SplayNodeInit(cbsSplay(cbs), cbsBlockTree(block));
AVERT(CBSBlock, block);
*blockReturn = block;
@@ -358,71 +422,81 @@ static Res cbsBlockAlloc(CBSBlock *blockReturn, CBS cbs, Range range)
return res;
}
-/* cbsBlockInsert -- insert a block into the splay tree */
+/* cbsBlockInsert -- insert a block into the tree */
static void cbsBlockInsert(CBS cbs, CBSBlock block)
{
- Res res;
+ Bool b;
AVERT(CBS, cbs);
AVERT(CBSBlock, block);
- METER_ACC(cbs->splaySearch, cbs->splayTreeSize);
- res = SplayTreeInsert(splayTreeOfCBS(cbs), splayNodeOfCBSBlock(block),
- keyOfCBSBlock(block));
- AVER(res == ResOK);
- STATISTIC(++cbs->splayTreeSize);
+ METER_ACC(cbs->treeSearch, cbs->treeSize);
+ b = SplayTreeInsert(cbsSplay(cbs), cbsBlockTree(block));
+ AVER(b);
+ STATISTIC(++cbs->treeSize);
+ cbs->size += CBSBlockSize(block);
}
-/* cbsInsertIntoTree -- Insert a range into the splay tree */
+/* cbsInsert -- Insert a range into the CBS
+ *
+ * See .
+ *
+ * .insert.alloc: Will only allocate a block if the range does not
+ * abut an existing range.
+ */
-static Res cbsInsertIntoTree(Range rangeReturn, CBS cbs, Range range)
+static Res cbsInsert(Range rangeReturn, Land land, Range range)
{
+ CBS cbs;
+ Bool b;
Res res;
Addr base, limit, newBase, newLimit;
- SplayNode leftSplay, rightSplay;
+ Tree leftSplay, rightSplay;
CBSBlock leftCBS, rightCBS;
Bool leftMerge, rightMerge;
Size oldSize;
AVER(rangeReturn != NULL);
- AVERT(CBS, cbs);
+ AVERT(Land, land);
AVERT(Range, range);
- AVER(RangeIsAligned(range, cbs->alignment));
+ AVER(RangeIsAligned(range, LandAlignment(land)));
+ cbs = cbsOfLand(land);
base = RangeBase(range);
limit = RangeLimit(range);
- METER_ACC(cbs->splaySearch, cbs->splayTreeSize);
- res = SplayTreeNeighbours(&leftSplay, &rightSplay,
- splayTreeOfCBS(cbs), (void *)&base);
- if (res != ResOK)
+ METER_ACC(cbs->treeSearch, cbs->treeSize);
+ b = SplayTreeNeighbours(&leftSplay, &rightSplay, cbsSplay(cbs), keyOfBaseVar(base));
+ if (!b) {
+ res = ResFAIL;
goto fail;
+ }
/* The two cases below are not quite symmetrical, because base was
* passed into the call to SplayTreeNeighbours(), but limit was not.
* So we know that if there is a left neighbour, then leftCBS->limit
- * <= base (this is ensured by cbsSplayCompare, which is the
- * comparison method on the splay tree). But if there is a right
+ * <= base (this is ensured by cbsCompare, which is the
+ * comparison method on the tree). But if there is a right
* neighbour, all we know is that base < rightCBS->base. But for the
* range to fit, we need limit <= rightCBS->base too. Hence the extra
* check and the possibility of failure in the second case.
*/
- if (leftSplay == NULL) {
+ if (leftSplay == TreeEMPTY) {
leftCBS = NULL;
leftMerge = FALSE;
} else {
- leftCBS = cbsBlockOfSplayNode(leftSplay);
+ leftCBS = cbsBlockOfTree(leftSplay);
AVER(leftCBS->limit <= base);
leftMerge = leftCBS->limit == base;
}
- if (rightSplay == NULL) {
+ if (rightSplay == TreeEMPTY) {
rightCBS = NULL;
rightMerge = FALSE;
} else {
- rightCBS = cbsBlockOfSplayNode(rightSplay);
+ rightCBS = cbsBlockOfTree(rightSplay);
if (rightCBS != NULL && limit > CBSBlockLimit(rightCBS)) {
res = ResFAIL;
goto fail;
@@ -470,52 +544,38 @@ static Res cbsInsertIntoTree(Range rangeReturn, CBS cbs, Range range)
}
-/* CBSInsert -- Insert a range into the CBS
+/* cbsDelete -- Remove a range from a CBS
*
- * See .
+ * See .
+ *
+ * .delete.alloc: Will only allocate a block if the range splits
+ * an existing range.
*/
-Res CBSInsert(Range rangeReturn, CBS cbs, Range range)
-{
- Res res;
-
- AVERT(CBS, cbs);
- cbsEnter(cbs);
-
- AVER(rangeReturn != NULL);
- AVERT(Range, range);
- AVER(RangeIsAligned(range, cbs->alignment));
-
- res = cbsInsertIntoTree(rangeReturn, cbs, range);
-
- cbsLeave(cbs);
- return res;
-}
-
-
-/* cbsDeleteFromTree -- delete blocks from the splay tree */
-
-static Res cbsDeleteFromTree(Range rangeReturn, CBS cbs, Range range)
+static Res cbsDelete(Range rangeReturn, Land land, Range range)
{
+ CBS cbs;
Res res;
CBSBlock cbsBlock;
- SplayNode splayNode;
+ Tree tree;
Addr base, limit, oldBase, oldLimit;
Size oldSize;
+ AVERT(Land, land);
+ cbs = cbsOfLand(land);
AVER(rangeReturn != NULL);
- AVERT(CBS, cbs);
AVERT(Range, range);
- AVER(RangeIsAligned(range, cbs->alignment));
+ AVER(RangeIsAligned(range, LandAlignment(land)));
base = RangeBase(range);
limit = RangeLimit(range);
- METER_ACC(cbs->splaySearch, cbs->splayTreeSize);
- res = SplayTreeSearch(&splayNode, splayTreeOfCBS(cbs), (void *)&base);
- if (res != ResOK)
+ METER_ACC(cbs->treeSearch, cbs->treeSize);
+ if (!SplayTreeFind(&tree, cbsSplay(cbs), keyOfBaseVar(base))) {
+ res = ResFAIL;
goto failSplayTreeSearch;
- cbsBlock = cbsBlockOfSplayNode(splayNode);
+ }
+ cbsBlock = cbsBlockOfTree(tree);
if (limit > cbsBlock->limit) {
res = ResFAIL;
@@ -570,111 +630,201 @@ static Res cbsDeleteFromTree(Range rangeReturn, CBS cbs, Range range)
}
-/* CBSDelete -- Remove a range from a CBS
- *
- * See .
- */
-
-Res CBSDelete(Range rangeReturn, CBS cbs, Range range)
+static Res cbsBlockDescribe(CBSBlock block, mps_lib_FILE *stream)
{
Res res;
- AVERT(CBS, cbs);
- cbsEnter(cbs);
+ if (stream == NULL)
+ return ResFAIL;
- AVER(rangeReturn != NULL);
- AVERT(Range, range);
- AVER(RangeIsAligned(range, cbs->alignment));
+ res = WriteF(stream, 0,
+ "[$P,$P)",
+ (WriteFP)block->base,
+ (WriteFP)block->limit,
+ NULL);
+ return res;
+}
+
+static Res cbsSplayNodeDescribe(Tree tree, mps_lib_FILE *stream)
+{
+ Res res;
- res = cbsDeleteFromTree(rangeReturn, cbs, range);
+ if (tree == TreeEMPTY)
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
- cbsLeave(cbs);
+ res = cbsBlockDescribe(cbsBlockOfTree(tree), stream);
return res;
}
-
-static Res cbsBlockDescribe(CBSBlock block, mps_lib_FILE *stream)
+static Res cbsFastBlockDescribe(CBSFastBlock block, mps_lib_FILE *stream)
{
Res res;
- if (stream == NULL) return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
- res = WriteF(stream,
+ res = WriteF(stream, 0,
"[$P,$P) {$U}",
- (WriteFP)block->base,
- (WriteFP)block->limit,
+ (WriteFP)block->cbsBlockStruct.base,
+ (WriteFP)block->cbsBlockStruct.limit,
(WriteFU)block->maxSize,
NULL);
return res;
}
-static Res cbsSplayNodeDescribe(SplayNode splayNode, mps_lib_FILE *stream)
+static Res cbsFastSplayNodeDescribe(Tree tree, mps_lib_FILE *stream)
{
Res res;
- if (splayNode == NULL) return ResFAIL;
- if (stream == NULL) return ResFAIL;
+ if (tree == TreeEMPTY)
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
- res = cbsBlockDescribe(cbsBlockOfSplayNode(splayNode), stream);
+ res = cbsFastBlockDescribe(cbsFastBlockOfTree(tree), stream);
return res;
}
+static Res cbsZonedBlockDescribe(CBSZonedBlock block, mps_lib_FILE *stream)
+{
+ Res res;
+
+ if (stream == NULL)
+ return ResFAIL;
-/* CBSIterate -- Iterate all blocks in CBS
+ res = WriteF(stream, 0,
+ "[$P,$P) {$U, $B}",
+ (WriteFP)block->cbsFastBlockStruct.cbsBlockStruct.base,
+ (WriteFP)block->cbsFastBlockStruct.cbsBlockStruct.limit,
+ (WriteFU)block->cbsFastBlockStruct.maxSize,
+ (WriteFB)block->zones,
+ NULL);
+ return res;
+}
+
+static Res cbsZonedSplayNodeDescribe(Tree tree, mps_lib_FILE *stream)
+{
+ Res res;
+
+ if (tree == TreeEMPTY)
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
+
+ res = cbsZonedBlockDescribe(cbsZonedBlockOfTree(tree), stream);
+ return res;
+}
+
+
+/* cbsIterate -- iterate over all blocks in CBS
*
- * This is not necessarily efficient.
- * See .
+ * See .
*/
-void CBSIterate(CBS cbs, CBSIterateMethod iterate,
- void *closureP, Size closureS)
+typedef struct CBSIterateClosure {
+ Land land;
+ LandVisitor visitor;
+ void *closureP;
+} CBSIterateClosure;
+
+static Bool cbsIterateVisit(Tree tree, void *closureP, Size closureS)
{
- SplayNode splayNode;
- SplayTree splayTree;
- CBSBlock cbsBlock;
+ CBSIterateClosure *closure = closureP;
+ Land land = closure->land;
+ CBSBlock cbsBlock = cbsBlockOfTree(tree);
+ RangeStruct range;
+ RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock));
+ return (*closure->visitor)(land, &range, closure->closureP, closureS);
+}
+static Bool cbsIterate(Land land, LandVisitor visitor,
+ void *closureP, Size closureS)
+{
+ CBS cbs;
+ SplayTree splay;
+ CBSIterateClosure closure;
+
+ AVERT(Land, land);
+ cbs = cbsOfLand(land);
AVERT(CBS, cbs);
- cbsEnter(cbs);
- AVER(FUNCHECK(iterate));
+ AVER(FUNCHECK(visitor));
- splayTree = splayTreeOfCBS(cbs);
+ splay = cbsSplay(cbs);
/* .splay-iterate.slow: We assume that splay tree iteration does */
/* searches and meter it. */
- METER_ACC(cbs->splaySearch, cbs->splayTreeSize);
- splayNode = SplayTreeFirst(splayTree, NULL);
- while(splayNode != NULL) {
- RangeStruct range;
- cbsBlock = cbsBlockOfSplayNode(splayNode);
- RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock));
- if (!(*iterate)(cbs, &range, closureP, closureS))
- break;
- METER_ACC(cbs->splaySearch, cbs->splayTreeSize);
- splayNode = SplayTreeNext(splayTree, splayNode, keyOfCBSBlock(cbsBlock));
- }
+ METER_ACC(cbs->treeSearch, cbs->treeSize);
- cbsLeave(cbs);
- return;
+ closure.land = land;
+ closure.visitor = visitor;
+ closure.closureP = closureP;
+ return TreeTraverse(SplayTreeRoot(splay), splay->compare, splay->nodeKey,
+ cbsIterateVisit, &closure, closureS);
}
-/* FindDeleteCheck -- check method for a FindDelete value */
+/* cbsIterateAndDelete -- iterate over all blocks in CBS
+ *
+ * See .
+ */
-Bool FindDeleteCheck(FindDelete findDelete)
+typedef struct CBSIterateAndDeleteClosure {
+ Land land;
+ LandDeleteVisitor visitor;
+ Bool cont;
+ void *closureP;
+} CBSIterateAndDeleteClosure;
+
+static Bool cbsIterateAndDeleteVisit(Tree tree, void *closureP, Size closureS)
{
- CHECKL(findDelete == FindDeleteNONE
- || findDelete == FindDeleteLOW
- || findDelete == FindDeleteHIGH
- || findDelete == FindDeleteENTIRE);
- UNUSED(findDelete); /* */
+ CBSIterateAndDeleteClosure *closure = closureP;
+ Land land = closure->land;
+ CBS cbs = cbsOfLand(land);
+ CBSBlock cbsBlock = cbsBlockOfTree(tree);
+ Bool deleteNode = FALSE;
+ RangeStruct range;
+
+ RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock));
+ if (closure->cont)
+ closure->cont = (*closure->visitor)(&deleteNode, land, &range,
+ closure->closureP, closureS);
+ if (deleteNode)
+ cbsBlockDestroy(cbs, cbsBlock);
+ return deleteNode;
+}
- return TRUE;
+static Bool cbsIterateAndDelete(Land land, LandDeleteVisitor visitor,
+ void *closureP, Size closureS)
+{
+ CBS cbs;
+ SplayTree splay;
+ CBSIterateAndDeleteClosure closure;
+
+ AVERT(Land, land);
+ cbs = cbsOfLand(land);
+ AVERT(CBS, cbs);
+ AVER(FUNCHECK(visitor));
+
+ splay = cbsSplay(cbs);
+ /* .splay-iterate.slow: We assume that splay tree iteration does */
+ /* searches and meter it. */
+ METER_ACC(cbs->treeSearch, cbs->treeSize);
+
+ closure.land = land;
+ closure.visitor = visitor;
+ closure.closureP = closureP;
+ closure.cont = TRUE;
+ TreeTraverseAndDelete(&splay->root, cbsIterateAndDeleteVisit,
+ &closure, closureS);
+ return closure.cont;
}
/* cbsFindDeleteRange -- delete appropriate range of block found */
static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn,
- CBS cbs, Range range, Size size,
+ Land land, Range range, Size size,
FindDelete findDelete)
{
Bool callDelete = TRUE;
@@ -682,11 +832,11 @@ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn,
AVER(rangeReturn != NULL);
AVER(oldRangeReturn != NULL);
- AVERT(CBS, cbs);
+ AVERT(Land, land);
AVERT(Range, range);
- AVER(RangeIsAligned(range, cbs->alignment));
+ AVER(RangeIsAligned(range, LandAlignment(land)));
AVER(size > 0);
- AVER(SizeIsAligned(size, cbs->alignment));
+ AVER(SizeIsAligned(size, LandAlignment(land)));
AVER(RangeSize(range) >= size);
AVERT(FindDelete, findDelete);
@@ -720,170 +870,345 @@ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn,
if (callDelete) {
Res res;
- res = cbsDeleteFromTree(oldRangeReturn, cbs, rangeReturn);
+ res = cbsDelete(oldRangeReturn, land, rangeReturn);
/* Can't have run out of memory, because all our callers pass in
- blocks that were just found in the splay tree, and we only
- deleted from one end of the block, so cbsDeleteFromTree did not
+ blocks that were just found in the tree, and we only
+ deleted from one end of the block, so cbsDelete did not
need to allocate a new block. */
AVER(res == ResOK);
+ } else {
+ RangeCopy(oldRangeReturn, rangeReturn);
}
}
/* CBSFindFirst -- find the first block of at least the given size */
-Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn,
- CBS cbs, Size size, FindDelete findDelete)
+static Bool cbsFindFirst(Range rangeReturn, Range oldRangeReturn,
+ Land land, Size size, FindDelete findDelete)
{
+ CBS cbs;
Bool found;
- SplayNode node;
+ Tree tree;
+ AVERT(Land, land);
+ cbs = cbsOfLand(land);
AVERT(CBS, cbs);
- cbsEnter(cbs);
+ AVER(IsLandSubclass(CBSLand(cbs), CBSFastLandClass));
AVER(rangeReturn != NULL);
AVER(oldRangeReturn != NULL);
AVER(size > 0);
- AVER(SizeIsAligned(size, cbs->alignment));
- AVER(cbs->fastFind);
+ AVER(SizeIsAligned(size, LandAlignment(land)));
AVERT(FindDelete, findDelete);
- METER_ACC(cbs->splaySearch, cbs->splayTreeSize);
- found = SplayFindFirst(&node, splayTreeOfCBS(cbs), &cbsTestNode,
+ METER_ACC(cbs->treeSearch, cbs->treeSize);
+ found = SplayFindFirst(&tree, cbsSplay(cbs), &cbsTestNode,
&cbsTestTree, NULL, size);
if (found) {
CBSBlock block;
RangeStruct range;
- block = cbsBlockOfSplayNode(node);
+ block = cbsBlockOfTree(tree);
AVER(CBSBlockSize(block) >= size);
RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block));
AVER(RangeSize(&range) >= size);
- cbsFindDeleteRange(rangeReturn, oldRangeReturn, cbs, &range,
+ cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range,
size, findDelete);
}
- cbsLeave(cbs);
return found;
}
+/* cbsFindInZones -- find a block of at least the given size that lies
+ * entirely within a zone set. (The first such block, if high is
+ * FALSE, or the last, if high is TRUE.)
+ */
+
+typedef struct cbsTestNodeInZonesClosureStruct {
+ Size size;
+ Arena arena;
+ ZoneSet zoneSet;
+ Addr base;
+ Addr limit;
+ Bool high;
+} cbsTestNodeInZonesClosureStruct, *cbsTestNodeInZonesClosure;
+
+static Bool cbsTestNodeInZones(SplayTree splay, Tree tree,
+ void *closureP, Size closureS)
+{
+ CBSBlock block = cbsBlockOfTree(tree);
+ cbsTestNodeInZonesClosure closure = closureP;
+ RangeInZoneSet search;
+
+ UNUSED(splay);
+ AVER(closureS == UNUSED_SIZE);
+ UNUSED(closureS);
+
+ search = closure->high ? RangeInZoneSetLast : RangeInZoneSetFirst;
+
+ return search(&closure->base, &closure->limit,
+ CBSBlockBase(block), CBSBlockLimit(block),
+ closure->arena, closure->zoneSet, closure->size);
+}
-/* CBSFindLast -- find the last block of at least the given size */
+static Bool cbsTestTreeInZones(SplayTree splay, Tree tree,
+ void *closureP, Size closureS)
+{
+ CBSFastBlock fastBlock = cbsFastBlockOfTree(tree);
+ CBSZonedBlock zonedBlock = cbsZonedBlockOfTree(tree);
+ cbsTestNodeInZonesClosure closure = closureP;
+
+ UNUSED(splay);
+ AVER(closureS == UNUSED_SIZE);
+ UNUSED(closureS);
+
+ return fastBlock->maxSize >= closure->size
+ && ZoneSetInter(zonedBlock->zones, closure->zoneSet) != ZoneSetEMPTY;
+}
+
+
+/* cbsFindLast -- find the last block of at least the given size */
-Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn,
- CBS cbs, Size size, FindDelete findDelete)
+static Bool cbsFindLast(Range rangeReturn, Range oldRangeReturn,
+ Land land, Size size, FindDelete findDelete)
{
+ CBS cbs;
Bool found;
- SplayNode node;
+ Tree tree;
+ AVERT(Land, land);
+ cbs = cbsOfLand(land);
AVERT(CBS, cbs);
- cbsEnter(cbs);
+ AVER(IsLandSubclass(CBSLand(cbs), CBSFastLandClass));
AVER(rangeReturn != NULL);
AVER(oldRangeReturn != NULL);
AVER(size > 0);
- AVER(SizeIsAligned(size, cbs->alignment));
- AVER(cbs->fastFind);
+ AVER(SizeIsAligned(size, LandAlignment(land)));
AVERT(FindDelete, findDelete);
- METER_ACC(cbs->splaySearch, cbs->splayTreeSize);
- found = SplayFindLast(&node, splayTreeOfCBS(cbs), &cbsTestNode,
+ METER_ACC(cbs->treeSearch, cbs->treeSize);
+ found = SplayFindLast(&tree, cbsSplay(cbs), &cbsTestNode,
&cbsTestTree, NULL, size);
if (found) {
CBSBlock block;
RangeStruct range;
- block = cbsBlockOfSplayNode(node);
+ block = cbsBlockOfTree(tree);
AVER(CBSBlockSize(block) >= size);
RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block));
AVER(RangeSize(&range) >= size);
- cbsFindDeleteRange(rangeReturn, oldRangeReturn, cbs, &range,
+ cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range,
size, findDelete);
}
- cbsLeave(cbs);
return found;
}
-/* CBSFindLargest -- find the largest block in the CBS */
+/* cbsFindLargest -- find the largest block in the CBS */
-Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn,
- CBS cbs, Size size, FindDelete findDelete)
+static Bool cbsFindLargest(Range rangeReturn, Range oldRangeReturn,
+ Land land, Size size, FindDelete findDelete)
{
+ CBS cbs;
Bool found = FALSE;
- SplayNode root;
- Bool notEmpty;
+ AVERT(Land, land);
+ cbs = cbsOfLand(land);
AVERT(CBS, cbs);
- cbsEnter(cbs);
+ AVER(IsLandSubclass(CBSLand(cbs), CBSFastLandClass));
AVER(rangeReturn != NULL);
AVER(oldRangeReturn != NULL);
- AVER(cbs->fastFind);
+ AVER(size > 0);
AVERT(FindDelete, findDelete);
- notEmpty = SplayRoot(&root, splayTreeOfCBS(cbs));
- if (notEmpty) {
+ if (!SplayTreeIsEmpty(cbsSplay(cbs))) {
RangeStruct range;
- CBSBlock block;
- SplayNode node = NULL; /* suppress "may be used uninitialized" */
+ Tree tree = TreeEMPTY; /* suppress "may be used uninitialized" */
Size maxSize;
- maxSize = cbsBlockOfSplayNode(root)->maxSize;
+ maxSize = cbsFastBlockOfTree(SplayTreeRoot(cbsSplay(cbs)))->maxSize;
if (maxSize >= size) {
- METER_ACC(cbs->splaySearch, cbs->splayTreeSize);
- found = SplayFindFirst(&node, splayTreeOfCBS(cbs), &cbsTestNode,
+ CBSBlock block;
+ METER_ACC(cbs->treeSearch, cbs->treeSize);
+ found = SplayFindFirst(&tree, cbsSplay(cbs), &cbsTestNode,
&cbsTestTree, NULL, maxSize);
AVER(found); /* maxSize is exact, so we will find it. */
- block = cbsBlockOfSplayNode(node);
+ block = cbsBlockOfTree(tree);
AVER(CBSBlockSize(block) >= maxSize);
RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block));
AVER(RangeSize(&range) >= maxSize);
- cbsFindDeleteRange(rangeReturn, oldRangeReturn, cbs, &range,
- maxSize, findDelete);
+ cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range,
+ size, findDelete);
}
}
- cbsLeave(cbs);
return found;
}
-/* CBSDescribe -- describe a CBS
+static Res cbsFindInZones(Bool *foundReturn, Range rangeReturn,
+ Range oldRangeReturn, Land land, Size size,
+ ZoneSet zoneSet, Bool high)
+{
+ CBS cbs;
+ CBSBlock block;
+ Tree tree;
+ cbsTestNodeInZonesClosureStruct closure;
+ Res res;
+ LandFindMethod landFind;
+ SplayFindFunction splayFind;
+ RangeStruct rangeStruct, oldRangeStruct;
+
+ AVER(foundReturn != NULL);
+ AVER(rangeReturn != NULL);
+ AVER(oldRangeReturn != NULL);
+ AVERT(Land, land);
+ cbs = cbsOfLand(land);
+ AVERT(CBS, cbs);
+ AVER(IsLandSubclass(CBSLand(cbs), CBSZonedLandClass));
+ /* AVERT(ZoneSet, zoneSet); */
+ AVERT(Bool, high);
+
+ landFind = high ? cbsFindLast : cbsFindFirst;
+ splayFind = high ? SplayFindLast : SplayFindFirst;
+
+ if (zoneSet == ZoneSetEMPTY)
+ goto fail;
+ if (zoneSet == ZoneSetUNIV) {
+ FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW;
+ *foundReturn = (*landFind)(rangeReturn, oldRangeReturn, land, size, fd);
+ return ResOK;
+ }
+ if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land)))
+ goto fail;
+
+ /* It would be nice if there were a neat way to eliminate all runs of
+ zones in zoneSet too small for size.*/
+
+ closure.arena = LandArena(land);
+ closure.zoneSet = zoneSet;
+ closure.size = size;
+ closure.high = high;
+ if (!(*splayFind)(&tree, cbsSplay(cbs),
+ cbsTestNodeInZones, cbsTestTreeInZones,
+ &closure, UNUSED_SIZE))
+ goto fail;
+
+ block = cbsBlockOfTree(tree);
+
+ AVER(CBSBlockBase(block) <= closure.base);
+ AVER(AddrOffset(closure.base, closure.limit) >= size);
+ AVER(ZoneSetSub(ZoneSetOfRange(LandArena(land), closure.base, closure.limit), zoneSet));
+ AVER(closure.limit <= CBSBlockLimit(block));
+
+ if (!high)
+ RangeInit(&rangeStruct, closure.base, AddrAdd(closure.base, size));
+ else
+ RangeInit(&rangeStruct, AddrSub(closure.limit, size), closure.limit);
+ res = cbsDelete(&oldRangeStruct, land, &rangeStruct);
+ if (res != ResOK)
+ /* not enough memory to split block */
+ return res;
+ RangeCopy(rangeReturn, &rangeStruct);
+ RangeCopy(oldRangeReturn, &oldRangeStruct);
+ *foundReturn = TRUE;
+ return ResOK;
+
+fail:
+ *foundReturn = FALSE;
+ return ResOK;
+}
+
+
+/* cbsDescribe -- describe a CBS
*
- * See .
+ * See .
*/
-Res CBSDescribe(CBS cbs, mps_lib_FILE *stream)
+static Res cbsDescribe(Land land, mps_lib_FILE *stream, Count depth)
{
+ CBS cbs;
Res res;
+ Res (*describe)(Tree, mps_lib_FILE *);
- if (!TESTT(CBS, cbs)) return ResFAIL;
- if (stream == NULL) return ResFAIL;
+ if (!TESTT(Land, land))
+ return ResFAIL;
+ cbs = cbsOfLand(land);
+ if (!TESTT(CBS, cbs))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
- res = WriteF(stream,
+ res = WriteF(stream, depth,
"CBS $P {\n", (WriteFP)cbs,
- " alignment: $U\n", (WriteFU)cbs->alignment,
- " blockPool: $P\n", (WriteFP)cbs->blockPool,
- " fastFind: $U\n", (WriteFU)cbs->fastFind,
- " inCBS: $U\n", (WriteFU)cbs->inCBS,
- " splayTreeSize: $U\n", (WriteFU)cbs->splayTreeSize,
+ " blockPool: $P\n", (WriteFP)cbsBlockPool(cbs),
+ " ownPool: $U\n", (WriteFU)cbs->ownPool,
+ " treeSize: $U\n", (WriteFU)cbs->treeSize,
NULL);
- if (res != ResOK) return res;
+ if (res != ResOK)
+ return res;
- res = SplayTreeDescribe(splayTreeOfCBS(cbs), stream, &cbsSplayNodeDescribe);
- if (res != ResOK) return res;
+ METER_WRITE(cbs->treeSearch, stream, depth + 2);
- res = METER_WRITE(cbs->splaySearch, stream);
- if (res != ResOK) return res;
+ if (IsLandSubclass(land, CBSZonedLandClass))
+ describe = cbsZonedSplayNodeDescribe;
+ else if (IsLandSubclass(land, CBSFastLandClass))
+ describe = cbsFastSplayNodeDescribe;
+ else
+ describe = cbsSplayNodeDescribe;
- res = WriteF(stream, "}\n", NULL);
+ res = SplayTreeDescribe(cbsSplay(cbs), stream, depth + 2, describe);
+ if (res != ResOK)
+ return res;
+
+ res = WriteF(stream, depth, "} CBS $P\n", (WriteFP)cbs, NULL);
+
+ res = WriteF(stream, 0, "}\n", NULL);
return res;
}
+DEFINE_LAND_CLASS(CBSLandClass, class)
+{
+ INHERIT_CLASS(class, LandClass);
+ class->name = "CBS";
+ class->size = sizeof(CBSStruct);
+ class->init = cbsInit;
+ class->finish = cbsFinish;
+ class->sizeMethod = cbsSize;
+ class->insert = cbsInsert;
+ class->delete = cbsDelete;
+ class->iterate = cbsIterate;
+ class->iterateAndDelete = cbsIterateAndDelete;
+ class->findFirst = cbsFindFirst;
+ class->findLast = cbsFindLast;
+ class->findLargest = cbsFindLargest;
+ class->findInZones = cbsFindInZones;
+ class->describe = cbsDescribe;
+ AVERT(LandClass, class);
+}
+
+DEFINE_LAND_CLASS(CBSFastLandClass, class)
+{
+ INHERIT_CLASS(class, CBSLandClass);
+ class->name = "FASTCBS";
+ class->init = cbsInitFast;
+ AVERT(LandClass, class);
+}
+
+DEFINE_LAND_CLASS(CBSZonedLandClass, class)
+{
+ INHERIT_CLASS(class, CBSFastLandClass);
+ class->name = "ZONEDCBS";
+ class->init = cbsInitZoned;
+ AVERT(LandClass, class);
+}
+
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2015 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/cbs.h b/code/cbs.h
index aba66cf5c1..a1496b3f77 100644
--- a/code/cbs.h
+++ b/code/cbs.h
@@ -10,51 +10,42 @@
#define cbs_h
#include "arg.h"
-#include "meter.h"
#include "mpmtypes.h"
+#include "mpmst.h"
#include "range.h"
#include "splay.h"
+typedef struct CBSBlockStruct *CBSBlock;
+typedef struct CBSBlockStruct {
+ TreeStruct treeStruct;
+ Addr base;
+ Addr limit;
+} CBSBlockStruct;
+
+typedef struct CBSFastBlockStruct *CBSFastBlock;
+typedef struct CBSFastBlockStruct {
+ struct CBSBlockStruct cbsBlockStruct;
+ Size maxSize; /* accurate maximum block size of sub-tree */
+} CBSFastBlockStruct;
+
+typedef struct CBSZonedBlockStruct *CBSZonedBlock;
+typedef struct CBSZonedBlockStruct {
+ struct CBSFastBlockStruct cbsFastBlockStruct;
+ ZoneSet zones; /* union zone set of all ranges in sub-tree */
+} CBSZonedBlockStruct;
typedef struct CBSStruct *CBS;
-typedef Bool (*CBSIterateMethod)(CBS cbs, Range range,
- void *closureP, Size closureS);
-
-
-#define CBSSig ((Sig)0x519CB599) /* SIGnature CBS */
-
-typedef struct CBSStruct {
- SplayTreeStruct splayTree;
- Count splayTreeSize;
- Pool blockPool;
- Align alignment;
- Bool fastFind;
- Bool inCBS; /* prevent reentrance */
- /* meters for sizes of search structures at each op */
- METER_DECL(splaySearch);
- Sig sig; /* sig at end because embeded */
-} CBSStruct;
extern Bool CBSCheck(CBS cbs);
+#define CBSLand(cbs) (&(cbs)->landStruct)
-extern Res CBSInit(Arena arena, CBS cbs, void *owner,
- Align alignment, Bool fastFind, ArgList args);
-extern void CBSFinish(CBS cbs);
-
-extern Res CBSInsert(Range rangeReturn, CBS cbs, Range range);
-extern Res CBSDelete(Range rangeReturn, CBS cbs, Range range);
-extern void CBSIterate(CBS cbs, CBSIterateMethod iterate,
- void *closureP, Size closureS);
-
-extern Res CBSDescribe(CBS cbs, mps_lib_FILE *stream);
-
-extern Bool CBSFindFirst(Range rangeReturn, Range oldRangeReturn,
- CBS cbs, Size size, FindDelete findDelete);
-extern Bool CBSFindLast(Range rangeReturn, Range oldRangeReturn,
- CBS cbs, Size size, FindDelete findDelete);
-extern Bool CBSFindLargest(Range rangeReturn, Range oldRangeReturn,
- CBS cbs, Size size, FindDelete findDelete);
+extern LandClass CBSLandClassGet(void);
+extern LandClass CBSFastLandClassGet(void);
+extern LandClass CBSZonedLandClassGet(void);
+extern const struct mps_key_s _mps_key_cbs_block_pool;
+#define CBSBlockPool (&_mps_key_cbs_block_pool)
+#define CBSBlockPool_FIELD pool
#endif /* cbs_h */
diff --git a/code/chain.h b/code/chain.h
index e47f8000c0..6dddf0f5e2 100644
--- a/code/chain.h
+++ b/code/chain.h
@@ -1,7 +1,7 @@
/* chain.h: GENERATION CHAINS
*
* $Id$
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*/
#ifndef chain_h
@@ -14,6 +14,8 @@
/* GenParamStruct -- structure for specifying generation parameters */
/* .gen-param: This structure must match . */
+typedef struct GenParamStruct *GenParam;
+
typedef struct GenParamStruct {
Size capacity; /* capacity in kB */
double mortality;
@@ -31,7 +33,6 @@ typedef struct GenDescStruct {
ZoneSet zones; /* zoneset for this generation */
Size capacity; /* capacity in kB */
double mortality;
- double proflow; /* predicted proportion of survivors promoted */
RingStruct locusRing; /* Ring of all PoolGen's in this GenDesc (locus) */
} GenDescStruct;
@@ -44,19 +45,19 @@ typedef struct PoolGenStruct *PoolGen;
typedef struct PoolGenStruct {
Sig sig;
- Serial nr; /* generation number */
Pool pool; /* pool this belongs to */
- Chain chain; /* chain this belongs to */
+ GenDesc gen; /* generation this belongs to */
/* link in ring of all PoolGen's in this GenDesc (locus) */
RingStruct genRing;
- Size totalSize; /* total size of segs in gen in this pool */
- Size newSize; /* size allocated since last GC */
- /* newSize when TraceCreate was called. This is used in the
- * TraceStartPoolGen event emitted at the start of a trace; at that
- * time, newSize has already been diminished by Whiten so we can't
- * use that value. TODO: This will not work well with multiple
- * traces. */
- Size newSizeAtCreate;
+
+ /* Accounting of memory in this generation for this pool */
+ STATISTIC_DECL(Size segs); /* number of segments */
+ Size totalSize; /* total (sum of segment sizes) */
+ STATISTIC_DECL(Size freeSize); /* unused (free or lost to fragmentation) */
+ Size newSize; /* allocated since last collection */
+ STATISTIC_DECL(Size oldSize); /* allocated prior to last collection */
+ Size newDeferredSize; /* new (but deferred) */
+ STATISTIC_DECL(Size oldDeferredSize); /* old (but deferred) */
} PoolGenStruct;
@@ -70,38 +71,49 @@ typedef struct mps_chain_s {
RingStruct chainRing; /* list of chains in the arena */
TraceSet activeTraces; /* set of traces collecting this chain */
size_t genCount; /* number of generations */
- GenDescStruct *gens; /* the array of generations */
+ GenDesc gens; /* the array of generations */
} ChainStruct;
+extern Bool GenDescCheck(GenDesc gen);
+extern Size GenDescNewSize(GenDesc gen);
+extern Size GenDescTotalSize(GenDesc gen);
+extern Res GenDescDescribe(GenDesc gen, mps_lib_FILE *stream, Count depth);
+
extern Res ChainCreate(Chain *chainReturn, Arena arena, size_t genCount,
- GenParamStruct *params);
+ GenParam params);
extern void ChainDestroy(Chain chain);
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);
-extern Res ChainAlloc(Seg *segReturn, Chain chain, Serial genNr,
- SegClass class, Size size, Pool pool,
- Bool withReservoirPermit, ArgList args);
-
-extern Bool PoolGenCheck(PoolGen gen);
-extern Res PoolGenInit(PoolGen gen, Chain chain, Serial nr, Pool pool);
-extern void PoolGenFinish(PoolGen gen);
-extern void PoolGenFlip(PoolGen gen);
-#define PoolGenNr(gen) ((gen)->nr)
-
+extern GenDesc ChainGen(Chain chain, Index gen);
+extern Res ChainDescribe(Chain chain, mps_lib_FILE *stream, Count depth);
+
+extern Bool PoolGenCheck(PoolGen pgen);
+extern Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool);
+extern void PoolGenFinish(PoolGen pgen);
+extern Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass class,
+ Size size, Bool withReservoirPermit, ArgList args);
+extern void PoolGenFree(PoolGen pgen, Seg seg, Size freeSize, Size oldSize,
+ Size newSize, Bool deferred);
+extern void PoolGenAccountForFill(PoolGen pgen, Size size, Bool deferred);
+extern void PoolGenAccountForEmpty(PoolGen pgen, Size unused, Bool deferred);
+extern void PoolGenAccountForAge(PoolGen pgen, Size aged, Bool deferred);
+extern void PoolGenAccountForReclaim(PoolGen pgen, Size reclaimed, Bool deferred);
+extern void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize);
+extern void PoolGenAccountForSegSplit(PoolGen pgen);
+extern void PoolGenAccountForSegMerge(PoolGen pgen);
+extern Res PoolGenDescribe(PoolGen gen, mps_lib_FILE *stream, Count depth);
#endif /* chain_h */
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/check.h b/code/check.h
index d4abc0fd80..a2450bebd9 100644
--- a/code/check.h
+++ b/code/check.h
@@ -1,7 +1,7 @@
/* check.h: ASSERTION INTERFACE
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (C) 2002 Global Graphics Software.
*
* .aver: This header defines a family of AVER and NOTREACHED macros.
@@ -52,7 +52,7 @@
#define ASSERT(cond, condstring) \
BEGIN \
if (cond) NOOP; else \
- mps_lib_assert_fail(__FILE__ , __LINE__, (condstring)); \
+ mps_lib_assert_fail(MPS_FILE, __LINE__, (condstring)); \
END
#define ASSERT_TYPECHECK(type, val) \
@@ -85,9 +85,6 @@
*
* TODO: Should also allow the check level variable to come from an
* environment variable.
- *
- * TODO: CheckLevelDEEP asserts on arena creation with bootstrapping
- * problems. It clearly hasn't been tried for a while. RB 2012-09-01
*/
enum {
@@ -284,10 +281,10 @@ extern unsigned CheckLevel;
/* COMPAT* -- type compatibility checking
*
* .check.macros: The COMPAT* macros use some C trickery to attempt to
- * verify that certain types and fields are equivalent. They do not
- * do a complete job. This trickery is justified by the security gained
- * in knowing that matches the MPM. See also
- * mail.richard.1996-08-07.09-49. [This paragraph is intended to
+ * verify that certain types and fields are equivalent. They do not do
+ * a complete job. This trickery is justified by the security gained
+ * in knowing that matches the MPM. See
+ * . [This paragraph is intended to
* satisfy rule.impl.trick.]
*/
@@ -327,7 +324,7 @@ extern unsigned CheckLevel;
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/clock.h b/code/clock.h
index 5e103ff439..3bf2a6deed 100644
--- a/code/clock.h
+++ b/code/clock.h
@@ -1,13 +1,12 @@
/* clock.h -- Fast clocks and timers
*
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* $Id$
*/
#ifndef clock_h
#define clock_h
-#include
#include "mpmtypes.h" /* for Word */
@@ -15,10 +14,6 @@
*
* On platforms that support it, we want to stamp events with a very cheap
* and fast high-resolution timer.
- *
- * TODO: This is a sufficiently complicated nest of ifdefs that it should
- * be quarantined in its own header with KEEP OUT signs attached.
- * RB 2012-09-11
*/
/* Microsoft C provides an intrinsic for the Intel rdtsc instruction.
@@ -34,6 +29,12 @@ typedef union EventClockUnion {
unsigned __int64 whole;
} EventClockUnion;
+#define EVENT_CLOCK_MAKE(lvalue, low, high) \
+ BEGIN \
+ ((EventClockUnion*)&(lvalue))->half.low = (low); \
+ ((EventClockUnion*)&(lvalue))->half.high = (high); \
+ END
+
#if _MSC_VER >= 1400
#pragma intrinsic(__rdtsc)
@@ -49,7 +50,7 @@ typedef union EventClockUnion {
using Microsoft Visual Studio 6 because of support for CodeView debugging
information. */
-#include /* KILL IT WITH FIRE! */
+#include "mpswin.h" /* KILL IT WITH FIRE! */
#define EVENT_CLOCK(lvalue) \
BEGIN \
@@ -70,8 +71,8 @@ typedef union EventClockUnion {
(*(EventClockUnion *)&(clock)).half.high, \
(*(EventClockUnion *)&(clock)).half.low)
-#define EVENT_CLOCK_WRITE(stream, clock) \
- WriteF(stream, "$W$W", \
+#define EVENT_CLOCK_WRITE(stream, depth, clock) \
+ WriteF(stream, depth, "$W$W", \
(*(EventClockUnion *)&(clock)).half.high, \
(*(EventClockUnion *)&(clock)).half.low, \
NULL)
@@ -90,8 +91,8 @@ typedef union EventClockUnion {
#endif
-#define EVENT_CLOCK_WRITE(stream, clock) \
- WriteF(stream, "$W", (WriteFW)(clock), NULL)
+#define EVENT_CLOCK_WRITE(stream, depth, clock) \
+ WriteF(stream, depth, "$W", (WriteFW)(clock), NULL)
#endif
@@ -106,6 +107,9 @@ typedef union EventClockUnion {
GCC or Clang. */
__extension__ typedef unsigned long long EventClock;
+#define EVENT_CLOCK_MAKE(lvalue, low, high) \
+ ((lvalue) = ((EventClock)(high) << 32) + ((EventClock)(low) & (0xfffffffful)))
+
/* Clang provides a cross-platform builtin for a fast timer, but it
was not available on Mac OS X 10.8 until the release of XCode 4.6.
*/
@@ -140,8 +144,8 @@ __extension__ typedef unsigned long long EventClock;
(unsigned long)((clock) >> 32), \
(unsigned long)((clock) & 0xffffffff))
-#define EVENT_CLOCK_WRITE(stream, clock) \
- WriteF(stream, "$W$W", (WriteFW)((clock) >> 32), (WriteFW)clock, NULL)
+#define EVENT_CLOCK_WRITE(stream, depth, clock) \
+ WriteF(stream, depth, "$W$W", (WriteFW)((clock) >> 32), (WriteFW)clock, NULL)
#endif /* Intel, GCC or Clang */
@@ -150,6 +154,9 @@ __extension__ typedef unsigned long long EventClock;
typedef mps_clock_t EventClock;
+#define EVENT_CLOCK_MAKE(lvalue, low, high) \
+ ((lvalue) = ((EventClock)(high) << 32) + ((EventClock)(low) & (0xfffffffful)))
+
#define EVENT_CLOCK(lvalue) \
BEGIN \
(lvalue) = mps_clock(); \
@@ -158,8 +165,8 @@ typedef mps_clock_t EventClock;
#define EVENT_CLOCK_PRINT(stream, clock) \
fprintf(stream, "%lu", (unsigned long)clock)
-#define EVENT_CLOCK_WRITE(stream, clock) \
- WriteF(stream, "$W", (WriteFW)clock, NULL)
+#define EVENT_CLOCK_WRITE(stream, depth, clock) \
+ WriteF(stream, depth, "$W", (WriteFW)clock, NULL)
#endif
@@ -169,7 +176,7 @@ typedef mps_clock_t EventClock;
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/comm.gmk b/code/comm.gmk
index 1fca1af7b0..dbd11f70d3 100644
--- a/code/comm.gmk
+++ b/code/comm.gmk
@@ -3,7 +3,7 @@
# comm.gmk: COMMON GNUMAKEFILE FRAGMENT
#
# $Id$
-# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
#
# DESCRIPTION
#
@@ -15,8 +15,8 @@
# Assumes the following variables and definitions:
# EXTRA_TARGETS a list of extra targets to build
# CFLAGSCOMPILER a list of flags for all compilations
-# CFLAGSSTRICT a list of flags for almost all compilations
-# CFLAGSLAX a list of flags for compilations which can't be as
+# CFLAGSCOMPILERSTRICT a list of flags for almost all compilations
+# CFLAGSCOMPILERLAX a list of flags for compilations which can't be as
# strict (e.g. because they have to include a third-
# party header file that isn't -ansi -pedantic).
# CFLAGSDEBUG a list of flags for compilations with maximum debug
@@ -36,7 +36,6 @@
# NOISY if defined and non-empty, causes commands to be emitted
# MPMPF platform-dependent C sources for the "mpm" part
# MPMS assembler sources for the "mpm" part (.s files)
-# MPMPS pre-processor assembler sources for the "mpm" part (.S files)
#
# %%PART: When adding a new part, add a new parameter above for the
# files included in the part.
@@ -108,31 +107,28 @@ endif
# These flags are included in all compilations.
# Avoid using PFMDEFS in platform makefiles, as they prevent the MPS being
# built with a simple command like "cc -c mps.c".
-CFLAGSCOMMON = $(PFMDEFS) $(CFLAGSCOMPILER) $(CFLAGSCOMPILERSTRICT)
+CFLAGSCOMMONSTRICT = $(PFMDEFS) $(CFLAGSCOMPILER) $(CFLAGSCOMPILERSTRICT)
CFLAGSCOMMONLAX = $(PFMDEFS) $(CFLAGSCOMPILER) $(CFLAGSCOMPILERLAX)
# %%VARIETY: When adding a new variety, define a macro containing the set
# of flags for the new variety.
# These flags are added to compilations for the indicated variety.
-CFRASH = -DCONFIG_VAR_RASH -DNDEBUG $(CFLAGSOPT)
-CFHOT = -DCONFIG_VAR_HOT -DNDEBUG $(CFLAGSOPT)
-CFCOOL = -DCONFIG_VAR_COOL $(CFLAGSDEBUG)
+CFRASH = -DCONFIG_VAR_RASH $(CFLAGSOPT)
+CFHOT = -DCONFIG_VAR_HOT $(CFLAGSOPT)
+CFCOOL = -DCONFIG_VAR_COOL $(CFLAGSDEBUG)
-# Bind CFLAGS to the appropriate set of flags for the variety.
-# %%VARIETY: When adding a new variety, add a test for the variety and set
-# CFLAGS here.
+# Bind CFLAGSVARIETY to the appropriate set of flags for the variety.
+# %%VARIETY: When adding a new variety, add a test for the variety and
+# set CFLAGSVARIETY here.
ifeq ($(VARIETY),rash)
-CFLAGS=$(CFLAGSCOMMON) $(CFRASH)
-CFLAGSLAX=$(CFLAGSCOMMONLAX) $(CFRASH)
+CFLAGSVARIETY=$(CFRASH)
else
ifeq ($(VARIETY),hot)
-CFLAGS=$(CFLAGSCOMMON) $(CFHOT)
-CFLAGSLAX=$(CFLAGSCOMMONLAX) $(CFHOT)
+CFLAGSVARIETY=$(CFHOT)
else
ifeq ($(VARIETY),cool)
-CFLAGS=$(CFLAGSCOMMON) $(CFCOOL)
-CFLAGSLAX=$(CFLAGSCOMMONLAX) $(CFCOOL)
+CFLAGSVARIETY=$(CFCOOL)
else
ifneq ($(VARIETY),)
$(error Variety "$(VARIETY)" not recognized: must be rash/hot/cool)
@@ -141,7 +137,8 @@ endif
endif
endif
-
+CFLAGSSTRICT=$(CFLAGSCOMMONSTRICT) $(CFLAGSVARIETY) $(CFLAGS)
+CFLAGSLAX=$(CFLAGSCOMMONLAX) $(CFLAGSVARIETY) $(CFLAGS)
ARFLAGS=rc$(ARFLAGSPFM)
@@ -159,18 +156,65 @@ POOLN = pooln.c
MV2 = poolmv2.c
MVFF = poolmvff.c
TESTLIB = testlib.c
+TESTTHR = testthrix.c
FMTDY = fmtdy.c fmtno.c
FMTDYTST = fmtdy.c fmtno.c fmtdytst.c
FMTHETST = fmthe.c fmtdy.c fmtno.c fmtdytst.c
+FMTSCM = fmtscheme.c
PLINTH = mpsliban.c mpsioan.c
-EVENTPROC = eventcnv.c table.c
-MPMCOMMON = abq.c arena.c arenacl.c arenavm.c arg.c boot.c bt.c \
- buffer.c cbs.c dbgpool.c dbgpooli.c event.c format.c \
- freelist.c global.c ld.c locus.c message.c meter.c mpm.c mpsi.c \
- pool.c poolabs.c poolmfs.c poolmrg.c poolmv.c protocol.c range.c \
- ref.c reserv.c ring.c root.c sa.c sac.c seg.c shield.c splay.c ss.c \
- table.c trace.c traceanc.c tract.c walk.c
-MPM = $(MPMCOMMON) $(MPMPF)
+MPMCOMMON = \
+ abq.c \
+ arena.c \
+ arenacl.c \
+ arenavm.c \
+ arg.c \
+ boot.c \
+ bt.c \
+ buffer.c \
+ cbs.c \
+ dbgpool.c \
+ dbgpooli.c \
+ event.c \
+ failover.c \
+ format.c \
+ freelist.c \
+ global.c \
+ land.c \
+ ld.c \
+ locus.c \
+ message.c \
+ meter.c \
+ mpm.c \
+ mpsi.c \
+ nailboard.c \
+ policy.c \
+ pool.c \
+ poolabs.c \
+ poolmfs.c \
+ poolmrg.c \
+ poolmv.c \
+ protocol.c \
+ range.c \
+ ref.c \
+ reserv.c \
+ ring.c \
+ root.c \
+ sa.c \
+ sac.c \
+ seg.c \
+ shield.c \
+ splay.c \
+ ss.c \
+ table.c \
+ trace.c \
+ traceanc.c \
+ tract.c \
+ tree.c \
+ version.c \
+ vm.c \
+ walk.c
+POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC)
+MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH)
# These map the source file lists onto object files and dependency files
@@ -182,35 +226,14 @@ MPM = $(MPMCOMMON) $(MPMPF)
ifdef VARIETY
MPMOBJ = $(MPM:%.c=$(PFM)/$(VARIETY)/%.o) \
$(MPMS:%.s=$(PFM)/$(VARIETY)/%.o)
-MPMDEP = $(MPM:%.c=$(PFM)/$(VARIETY)/%.d)
-AMCOBJ = $(AMC:%.c=$(PFM)/$(VARIETY)/%.o)
-AMCDEP = $(AMC:%.c=$(PFM)/$(VARIETY)/%.d)
-AMSOBJ = $(AMS:%.c=$(PFM)/$(VARIETY)/%.o)
-AMSDEP = $(AMS:%.c=$(PFM)/$(VARIETY)/%.d)
-AWLOBJ = $(AWL:%.c=$(PFM)/$(VARIETY)/%.o)
-AWLDEP = $(AWL:%.c=$(PFM)/$(VARIETY)/%.d)
-LOOBJ = $(LO:%.c=$(PFM)/$(VARIETY)/%.o)
-LODEP = $(LO:%.c=$(PFM)/$(VARIETY)/%.d)
-SNCOBJ = $(SNC:%.c=$(PFM)/$(VARIETY)/%.o)
-SNCDEP = $(SNC:%.c=$(PFM)/$(VARIETY)/%.d)
-POOLNOBJ = $(POOLN:%.c=$(PFM)/$(VARIETY)/%.o)
-POOLNDEP = $(POOLN:%.c=$(PFM)/$(VARIETY)/%.d)
-MV2OBJ = $(MV2:%.c=$(PFM)/$(VARIETY)/%.o)
-MV2DEP = $(MV2:%.c=$(PFM)/$(VARIETY)/%.d)
-MVFFOBJ = $(MVFF:%.c=$(PFM)/$(VARIETY)/%.o)
-MVFFDEP = $(MVFF:%.c=$(PFM)/$(VARIETY)/%.d)
-
-TESTLIBOBJ = $(TESTLIB:%.c=$(PFM)/$(VARIETY)/%.o)
-TESTLIBDEP = $(TESTLIB:%.c=$(PFM)/$(VARIETY)/%.d)
FMTDYOBJ = $(FMTDY:%.c=$(PFM)/$(VARIETY)/%.o)
-FMTDYDEP = $(FMTDY:%.c=$(PFM)/$(VARIETY)/%.d)
FMTDYTSTOBJ = $(FMTDYTST:%.c=$(PFM)/$(VARIETY)/%.o)
FMTHETSTOBJ = $(FMTHETST:%.c=$(PFM)/$(VARIETY)/%.o)
-FMTHETSTDEP = $(FMTHETST:%.c=$(PFM)/$(VARIETY)/%.d)
+FMTSCMOBJ = $(FMTSCM:%.c=$(PFM)/$(VARIETY)/%.o)
PLINTHOBJ = $(PLINTH:%.c=$(PFM)/$(VARIETY)/%.o)
-PLINTHDEP = $(PLINTH:%.c=$(PFM)/$(VARIETY)/%.d)
-EVENTPROCOBJ = $(EVENTPROC:%.c=$(PFM)/$(VARIETY)/%.o)
-EVENTPROCDEP = $(EVENTPROC:%.c=$(PFM)/$(VARIETY)/%.d)
+POOLNOBJ = $(POOLN:%.c=$(PFM)/$(VARIETY)/%.o)
+TESTLIBOBJ = $(TESTLIB:%.c=$(PFM)/$(VARIETY)/%.o)
+TESTTHROBJ = $(TESTTHR:%.c=$(PFM)/$(VARIETY)/%.o)
endif
@@ -221,30 +244,58 @@ endif
LIB_TARGETS=mps.a mpsplan.a
-# If it is suitable for running regularly (for example, after every
-# build) as an automated test case, add it to AUTO_TEST_TARGETS.
-
-AUTO_TEST_TARGETS=abqtest amcss amcsshe amcssth amsss amssshe apss \
- arenacv awlut awluthe awlutth btcv exposet0 expt825 fbmtest finalcv \
- finaltest fotest lockcov locv messtest mpmss mpsicv mv2test \
- poolncv qs sacss segsmss steptest walkt0 zmess
-
-# If it is not runnable as an automated test case, but is buildable,
-# add it to OTHER_TEST_TARGETS with a note.
-#
-# bttest and teletest -- interactive and so cannot be run unattended.
-# zcoll -- takes too long to be useful as a regularly run smoke test.
-
-OTHER_TEST_TARGETS=bttest teletest zcoll
+# Test executables go in TEST_TARGETS.
+
+TEST_TARGETS=\
+ abqtest \
+ airtest \
+ amcss \
+ amcsshe \
+ amcssth \
+ amsss \
+ amssshe \
+ apss \
+ arenacv \
+ awlut \
+ awluthe \
+ awlutth \
+ btcv \
+ bttest \
+ djbench \
+ exposet0 \
+ expt825 \
+ finalcv \
+ finaltest \
+ fotest \
+ gcbench \
+ landtest \
+ locbwcss \
+ lockcov \
+ lockut \
+ locusss \
+ locv \
+ messtest \
+ mpmss \
+ mpsicv \
+ mv2test \
+ nailboardtest \
+ poolncv \
+ qs \
+ sacss \
+ segsmss \
+ steptest \
+ teletest \
+ walkt0 \
+ zcoll \
+ zmess
# This target records programs that we were once able to build but
# can't at the moment:
-#
-# replay -- depends on the EPVM pool.
-UNBUILDABLE_TARGETS=replay
+UNBUILDABLE_TARGETS=\
+ replay # depends on the EPVM pool
-ALL_TARGETS=$(LIB_TARGETS) $(AUTO_TEST_TARGETS) $(OTHER_TEST_TARGETS) $(EXTRA_TARGETS)
+ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS)
# == Pseudo-targets ==
@@ -252,15 +303,24 @@ ALL_TARGETS=$(LIB_TARGETS) $(AUTO_TEST_TARGETS) $(OTHER_TEST_TARGETS) $(EXTRA_TA
all: $(ALL_TARGETS)
-# Run the automated tests.
+# == Automated test suites ==
+#
+# testrun = "smoke test", fast enough to run before every commit
+# testci = continuous integration tests, must be known good
+# testall = all test cases, for ensuring quality of a release
+# testansi = tests that run on the generic ("ANSI") platform
+# testpollnone = tests that run on the generic platform with CONFIG_POLL_NONE
+
+TEST_SUITES=testrun testci testall testansi testpollnone
+
+$(addprefix $(PFM)/$(VARIETY)/,$(TEST_SUITES)): $(TEST_TARGETS)
+ ../tool/testrun.sh -s "$(notdir $@)" "$(PFM)/$(VARIETY)"
-testrun: $(AUTO_TEST_TARGETS)
- ../tool/testrun.sh $(addprefix $(PFM)/$(VARIETY)/,$(AUTO_TEST_TARGETS))
# These convenience targets allow one to type "make foo" to build target
# foo in selected varieties (or none, for the latter rule).
-$(ALL_TARGETS): phony
+$(ALL_TARGETS) $(TEST_SUITES): phony
ifdef VARIETY
$(MAKE) -f $(PFM).gmk TARGET=$@ variety
else
@@ -275,17 +335,25 @@ clean: phony
$(ECHO) "$(PFM): $@"
rm -rf "$(PFM)"
-# "target" builds some varieties of the target named in the TARGET macro.
+# "target" builds some varieties of the target named in the TARGET
+# macro.
+#
# %%VARIETY: When adding a new target, optionally add a recursive make call
# for the new variety, if it should be built by default. It probably
# shouldn't without a product design decision and an update of the readme
# and build manual!
+#
+# Note that we build VARIETY=cool before VARIETY=hot because
+# the former doesn't need to optimize and so detects errors more
+# quickly; and because the former uses file-at-a-time compilation and
+# so can pick up where it left off instead of having to start from the
+# beginning of mps.c
ifdef TARGET
ifndef VARIETY
target: phony
- $(MAKE) -f $(PFM).gmk VARIETY=hot variety
$(MAKE) -f $(PFM).gmk VARIETY=cool variety
+ $(MAKE) -f $(PFM).gmk VARIETY=hot variety
endif
endif
@@ -321,10 +389,7 @@ endif
$(PFM)/rash/mps.a: $(PFM)/rash/mps.o
$(PFM)/hot/mps.a: $(PFM)/hot/mps.o
-
-$(PFM)/cool/mps.a: \
- $(MPMOBJ) $(AMCOBJ) $(AMSOBJ) $(AWLOBJ) $(LOOBJ) $(SNCOBJ) \
- $(MV2OBJ) $(MVFFOBJ) $(PLINTHOBJ) $(POOLNOBJ)
+$(PFM)/cool/mps.a: $(MPMOBJ)
# OTHER GENUINE TARGETS
@@ -341,6 +406,9 @@ ifdef VARIETY
$(PFM)/$(VARIETY)/abqtest: $(PFM)/$(VARIETY)/abqtest.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+$(PFM)/$(VARIETY)/airtest: $(PFM)/$(VARIETY)/airtest.o \
+ $(FMTSCMOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+
$(PFM)/$(VARIETY)/amcss: $(PFM)/$(VARIETY)/amcss.o \
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
@@ -348,7 +416,7 @@ $(PFM)/$(VARIETY)/amcsshe: $(PFM)/$(VARIETY)/amcsshe.o \
$(FMTHETSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
$(PFM)/$(VARIETY)/amcssth: $(PFM)/$(VARIETY)/amcssth.o \
- $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a
$(PFM)/$(VARIETY)/amsss: $(PFM)/$(VARIETY)/amsss.o \
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
@@ -369,7 +437,7 @@ $(PFM)/$(VARIETY)/awluthe: $(PFM)/$(VARIETY)/awluthe.o \
$(FMTHETSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
$(PFM)/$(VARIETY)/awlutth: $(PFM)/$(VARIETY)/awlutth.o \
- $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a
$(PFM)/$(VARIETY)/btcv: $(PFM)/$(VARIETY)/btcv.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
@@ -377,15 +445,15 @@ $(PFM)/$(VARIETY)/btcv: $(PFM)/$(VARIETY)/btcv.o \
$(PFM)/$(VARIETY)/bttest: $(PFM)/$(VARIETY)/bttest.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+$(PFM)/$(VARIETY)/djbench: $(PFM)/$(VARIETY)/djbench.o \
+ $(TESTLIBOBJ) $(TESTTHROBJ)
+
$(PFM)/$(VARIETY)/exposet0: $(PFM)/$(VARIETY)/exposet0.o \
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
$(PFM)/$(VARIETY)/expt825: $(PFM)/$(VARIETY)/expt825.o \
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
-$(PFM)/$(VARIETY)/fbmtest: $(PFM)/$(VARIETY)/fbmtest.o \
- $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
-
$(PFM)/$(VARIETY)/finalcv: $(PFM)/$(VARIETY)/finalcv.o \
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
@@ -395,9 +463,24 @@ $(PFM)/$(VARIETY)/finaltest: $(PFM)/$(VARIETY)/finaltest.o \
$(PFM)/$(VARIETY)/fotest: $(PFM)/$(VARIETY)/fotest.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+$(PFM)/$(VARIETY)/gcbench: $(PFM)/$(VARIETY)/gcbench.o \
+ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ)
+
+$(PFM)/$(VARIETY)/landtest: $(PFM)/$(VARIETY)/landtest.o \
+ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+
+$(PFM)/$(VARIETY)/locbwcss: $(PFM)/$(VARIETY)/locbwcss.o \
+ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+
$(PFM)/$(VARIETY)/lockcov: $(PFM)/$(VARIETY)/lockcov.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+$(PFM)/$(VARIETY)/lockut: $(PFM)/$(VARIETY)/lockut.o \
+ $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a
+
+$(PFM)/$(VARIETY)/locusss: $(PFM)/$(VARIETY)/locusss.o \
+ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+
$(PFM)/$(VARIETY)/locv: $(PFM)/$(VARIETY)/locv.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
@@ -413,9 +496,12 @@ $(PFM)/$(VARIETY)/mpsicv: $(PFM)/$(VARIETY)/mpsicv.o \
$(PFM)/$(VARIETY)/mv2test: $(PFM)/$(VARIETY)/mv2test.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
-$(PFM)/$(VARIETY)/poolncv: $(PFM)/$(VARIETY)/poolncv.o \
+$(PFM)/$(VARIETY)/nailboardtest: $(PFM)/$(VARIETY)/nailboardtest.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+$(PFM)/$(VARIETY)/poolncv: $(PFM)/$(VARIETY)/poolncv.o \
+ $(POOLNOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
+
$(PFM)/$(VARIETY)/qs: $(PFM)/$(VARIETY)/qs.o \
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
@@ -465,11 +551,11 @@ endif
# Object files
-define run-cc
+define run-cc-strict
$(ECHO) "$(PFM): $@"
mkdir -p $(PFM)
mkdir -p $(PFM)/$(VARIETY)
-$(CC) $(CFLAGS) -c -o $@ $<
+$(CC) $(CFLAGSSTRICT) -c -o $@ $<
endef
define run-cc-lax
@@ -481,16 +567,16 @@ endef
# .rule.c-to-o:
$(PFM)/$(VARIETY)/%.o: %.c
- $(run-cc)
+ $(run-cc-strict)
$(PFM)/$(VARIETY)/eventsql.o: eventsql.c
$(run-cc-lax)
$(PFM)/$(VARIETY)/%.o: %.s
- $(run-cc)
+ $(run-cc-strict)
$(PFM)/$(VARIETY)/%.o: %.S
- $(run-cc)
+ $(run-cc-strict)
# Dependencies
#
@@ -517,18 +603,28 @@ else
ifeq ($(VARIETY),hot)
include $(PFM)/$(VARIETY)/mps.d
else
-# %%PART: When adding a new part, add the dependency file macro for the new
-# part here.
-include $(MPMDEP) $(AMSDEP) $(AMCDEP) $(LODEP) \
- $(AWLDEP) $(POOLNDEP) $(TESTLIBDEP) $(FMTDYDEP) $(FMTHETSTDEP) \
- $(PLINTHDEP) $(EVENTPROCDEP)
-endif
-endif
-
-endif
-endif
-
-endif
+include $(MPM:%.c=$(PFM)/$(VARIETY)/%.d)
+endif # VARIETY != hot
+endif # VARIETY != rash
+
+# %%PART: When adding a new part, add the dependencies file for the
+# new part here.
+include \
+ $(FMTDY:%.c=$(PFM)/$(VARIETY)/%.d) \
+ $(FMTDYTST:%.c=$(PFM)/$(VARIETY)/%.d) \
+ $(FMTHETST:%.c=$(PFM)/$(VARIETY)/%.d) \
+ $(FMTSCM:%.c=$(PFM)/$(VARIETY)/%.d) \
+ $(PLINTH:%.c=$(PFM)/$(VARIETY)/%.d) \
+ $(POOLN:%.c=$(PFM)/$(VARIETY)/%.d) \
+ $(TESTLIB:%.c=$(PFM)/$(VARIETY)/%.d) \
+ $(TESTTHR:%.c=$(PFM)/$(VARIETY)/%.d) \
+ $(EXTRA_TARGETS:mps%=$(PFM)/$(VARIETY)/%.d) \
+ $(TEST_TARGETS:%=$(PFM)/$(VARIETY)/%.d)
+
+endif # !defined TARGET
+endif # !defined VARIETY
+
+endif # !defined gendep
# Library
@@ -539,19 +635,18 @@ endif
$(PFM)/$(VARIETY)/%.a:
$(ECHO) "$(PFM): $@"
rm -f $@
- $(CC) $(CFLAGS) -c -o $(PFM)/$(VARIETY)/version.o version.c
- $(AR) $(ARFLAGS) $@ $^ $(PFM)/$(VARIETY)/version.o
+ $(AR) $(ARFLAGS) $@ $^
$(RANLIB) $@
# Executable
$(PFM)/$(VARIETY)/%:
$(ECHO) "$(PFM): $@"
- $(CC) $(CFLAGS) $(LINKFLAGS) -o $@ $^ $(LIBS)
+ $(CC) $(CFLAGSSTRICT) $(LINKFLAGS) -o $@ $^ $(LIBS)
$(PFM)/$(VARIETY)/mpseventsql:
$(ECHO) "$(PFM): $@"
- $(CC) $(CFLAGS) $(LINKFLAGS) -o $@ $^ $(LIBS) -lsqlite3
+ $(CC) $(CFLAGSLAX) $(LINKFLAGS) -o $@ $^ $(LIBS) -lsqlite3
# Special targets for development
@@ -567,7 +662,7 @@ find-puns: phony
# C. COPYRIGHT AND LICENSE
#
-# Copyright (c) 2001-2013 Ravenbrook Limited .
+# Copyright (c) 2001-2014 Ravenbrook Limited .
# All rights reserved. This is an open source license. Contact
# Ravenbrook for commercial licensing options.
#
diff --git a/code/commpost.nmk b/code/commpost.nmk
index 808307b3b5..201094905b 100644
--- a/code/commpost.nmk
+++ b/code/commpost.nmk
@@ -1,7 +1,7 @@
# commpost.nmk: SECOND COMMON FRAGMENT FOR PLATFORMS USING NMAKE -*- makefile -*-
#
# $Id$
-# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
#
# DESCRIPTION
#
@@ -24,10 +24,13 @@ $(ALL_TARGETS) $(OPTIONAL_TARGETS):
# "clean" removes the directory containing the build results.
# Depends on there being no file called "clean".
+# Note that we suspend error processing on the if line because rmdir
+# sometimes exits with an error and the message "The directory is not
+# empty" even if the /s option is given. See job003854.
clean:
$(ECHO) $(PFM): $@
- -echo y | rmdir/s $(PFM)
+ -if exist $(PFM) rmdir /q /s $(PFM)
# target target
# %%VARIETY: When adding a new variety, optionally, add a recursive make
@@ -39,8 +42,8 @@ clean:
!IFDEF TARGET
!IFNDEF VARIETY
target:
- $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot variety
$(MAKE) /nologo /f $(PFM).nmk VARIETY=cool variety
+ $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot variety
!ENDIF
!ENDIF
@@ -53,21 +56,80 @@ variety: $(PFM)\$(VARIETY)\$(TARGET)
!ENDIF
!ENDIF
-mpsicv.cov:
- $(MAKE) /nologo /f $(PFM).nmk TARGET=$@ VARIETY=cv variety
-
-# testrun
+# testrun testci testall testansi testpollnone
# Runs automated test cases.
-testrun: $(AUTO_TEST_TARGETS)
+testrun testci testall testansi testpollnone: $(TEST_TARGETS)
!IFDEF VARIETY
- set MPS_TESTLIB_NOABORT=true
- ..\tool\testrun.bat $(PFM) $(VARIETY) $(AUTO_TEST_TARGETS)
+ ..\tool\testrun.bat $(PFM) $(VARIETY) $@
!ELSE
- $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot testrun
- $(MAKE) /nologo /f $(PFM).nmk VARIETY=cool testrun
+ $(MAKE) /nologo /f $(PFM).nmk VARIETY=cool $@
+ $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot $@
+!ENDIF
+
+
+# FLAGS AMALGAMATION
+#
+# %%VARIETY: When adding a new variety, add the following macros that
+# expand to sets of flags that the variety should use:
+#
+# CFLAGS -- when compiling C;
+# CFLAGSSQL -- when compiling mpseventsql;
+# LINKFLAGS -- when building executables;
+# LIBFLAGS -- when building libraries.
+
+!IF "$(VARIETY)" == "hot"
+CFLAGS=$(CFLAGSCOMMONPRE) $(CFHOT) $(CFLAGSCOMMONPOST)
+CFLAGSSQL=$(CFLAGSSQLPRE) $(CFHOT) $(CFLAGSSQLPOST)
+LINKFLAGS=$(LINKFLAGSCOMMON) $(LFHOT)
+LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSHOT)
+
+!ELSEIF "$(VARIETY)" == "cool"
+CFLAGS=$(CFLAGSCOMMONPRE) $(CFCOOL) $(CFLAGSCOMMONPOST)
+CFLAGSSQL=$(CFLAGSSQLPRE) $(CFCOOL) $(CFLAGSSQLPOST)
+LINKFLAGS=$(LINKFLAGSCOMMON) $(LFCOOL)
+LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSCOOL)
+
+!ELSEIF "$(VARIETY)" == "rash"
+CFLAGS=$(CFLAGSCOMMONPRE) $(CFRASH) $(CFLAGSCOMMONPOST)
+CFLAGSSQL=$(CFLAGSSQLPRE) $(CFRASH) $(CFLAGSSQLPOST)
+LINKFLAGS=$(LINKFLAGSCOMMON) $(LFRASH)
+LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSRASH)
+
+!ENDIF
+
+
+# SOURCE TO OBJECT FILE MAPPINGS
+#
+# %%PART: When adding a new part, add new macros which expand to the object
+# files included in the part
+#
+# Note: nmake doesn't expand variables within a string replacement
+# operation. We work around this by writing out a temporary makefile
+# and including it.
+
+TEMPMAKE=$(TEMP)\mps.nmk
+!IF [echo MPMOBJ0 = $$(MPM:[=$(PFM)\$(VARIETY)\) > $(TEMPMAKE)] == 0 \
+ && [echo FMTDYOBJ0 = $$(FMTDY:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \
+ && [echo FMTTESTOBJ0 = $$(FMTTEST:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \
+ && [echo FMTSCHEMEOBJ0 = $$(FMTSCHEME:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \
+ && [echo POOLNOBJ0 = $$(POOLN:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \
+ && [echo TESTLIBOBJ0 = $$(TESTLIB:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \
+ && [echo TESTTHROBJ0 = $$(TESTTHR:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0
+!INCLUDE $(TEMPMAKE)
+!IF [del $(TEMPMAKE)] != 0
+!ERROR Failed to delete $(TEMPMAKE)
+!ENDIF
!ENDIF
+MPMOBJ = $(MPMOBJ0:]=.obj)
+FMTDYOBJ = $(FMTDYOBJ0:]=.obj)
+FMTTESTOBJ = $(FMTTESTOBJ0:]=.obj)
+FMTSCHEMEOBJ = $(FMTSCHEMEOBJ0:]=.obj)
+POOLNOBJ = $(POOLNOBJ0:]=.obj)
+TESTLIBOBJ = $(TESTLIBOBJ0:]=.obj)
+TESTTHROBJ = $(TESTTHROBJ0:]=.obj)
+
# THE MPS LIBRARY
#
@@ -96,12 +158,9 @@ $(PFM)\hot\mps.lib: $(PFM)\hot\mps.obj
$(ECHO) $@
$(LIBMAN) $(LIBFLAGS) /OUT:$@ $**
-$(PFM)\cool\mps.lib: \
- $(MPMOBJ) $(AMCOBJ) $(AMSOBJ) $(AWLOBJ) $(LOOBJ) $(SNCOBJ) \
- $(MVFFOBJ) $(PLINTHOBJ) $(POOLNOBJ)
+$(PFM)\cool\mps.lib: $(MPMOBJ)
$(ECHO) $@
- cl /c $(CFLAGS) /Fd$(PFM)\$(VARIETY)\ /Fo$(PFM)\$(VARIETY)\version.o version.c
- $(LIBMAN) $(LIBFLAGS) /OUT:$@ $** $(PFM)\$(VARIETY)\version.o
+ $(LIBMAN) $(LIBFLAGS) /OUT:$@ $**
# OTHER GENUINE TARGETS
@@ -118,6 +177,9 @@ $(PFM)\cool\mps.lib: \
$(PFM)\$(VARIETY)\abqtest.exe: $(PFM)\$(VARIETY)\abqtest.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
+$(PFM)\$(VARIETY)\airtest.exe: $(PFM)\$(VARIETY)\airtest.obj \
+ $(PFM)\$(VARIETY)\mps.lib $(FMTSCHEMEOBJ) $(TESTLIBOBJ)
+
$(PFM)\$(VARIETY)\amcss.exe: $(PFM)\$(VARIETY)\amcss.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
@@ -125,7 +187,7 @@ $(PFM)\$(VARIETY)\amcsshe.exe: $(PFM)\$(VARIETY)\amcsshe.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
$(PFM)\$(VARIETY)\amcssth.exe: $(PFM)\$(VARIETY)\amcssth.obj \
- $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
+ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ)
$(PFM)\$(VARIETY)\amsss.exe: $(PFM)\$(VARIETY)\amsss.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
@@ -147,6 +209,10 @@ $(PFM)\$(VARIETY)\awluthe.exe: $(PFM)\$(VARIETY)\awluthe.obj \
$(FMTTESTOBJ) \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
+$(PFM)\$(VARIETY)\awlutth.exe: $(PFM)\$(VARIETY)\awlutth.obj \
+ $(FMTTESTOBJ) \
+ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(TESTTHROBJ)
+
$(PFM)\$(VARIETY)\btcv.exe: $(PFM)\$(VARIETY)\btcv.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
@@ -156,15 +222,15 @@ $(PFM)\$(VARIETY)\bttest.exe: $(PFM)\$(VARIETY)\bttest.obj \
$(PFM)\$(VARIETY)\cvmicv.exe: $(PFM)\$(VARIETY)\cvmicv.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
+$(PFM)\$(VARIETY)\djbench.exe: $(PFM)\$(VARIETY)\djbench.obj \
+ $(TESTLIBOBJ) $(TESTTHROBJ)
+
$(PFM)\$(VARIETY)\exposet0.exe: $(PFM)\$(VARIETY)\exposet0.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
$(PFM)\$(VARIETY)\expt825.exe: $(PFM)\$(VARIETY)\expt825.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
-$(PFM)\$(VARIETY)\fbmtest.exe: $(PFM)\$(VARIETY)\fbmtest.obj \
- $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
-
$(PFM)\$(VARIETY)\finalcv.exe: $(PFM)\$(VARIETY)\finalcv.obj \
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
@@ -174,14 +240,20 @@ $(PFM)\$(VARIETY)\finaltest.exe: $(PFM)\$(VARIETY)\finaltest.obj \
$(PFM)\$(VARIETY)\fotest.exe: $(PFM)\$(VARIETY)\fotest.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
+$(PFM)\$(VARIETY)\gcbench.exe: $(PFM)\$(VARIETY)\gcbench.obj \
+ $(FMTTESTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ)
+
+$(PFM)\$(VARIETY)\landtest.exe: $(PFM)\$(VARIETY)\landtest.obj \
+ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
+
$(PFM)\$(VARIETY)\locbwcss.exe: $(PFM)\$(VARIETY)\locbwcss.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
$(PFM)\$(VARIETY)\lockcov.exe: $(PFM)\$(VARIETY)\lockcov.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
-$(PFM)\$(VARIETY)\lockutw3.exe: $(PFM)\$(VARIETY)\lockutw3.obj \
- $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
+$(PFM)\$(VARIETY)\lockut.exe: $(PFM)\$(VARIETY)\lockut.obj \
+ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(TESTTHROBJ)
$(PFM)\$(VARIETY)\locusss.exe: $(PFM)\$(VARIETY)\locusss.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
@@ -201,9 +273,12 @@ $(PFM)\$(VARIETY)\mpsicv.exe: $(PFM)\$(VARIETY)\mpsicv.obj \
$(PFM)\$(VARIETY)\mv2test.exe: $(PFM)\$(VARIETY)\mv2test.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
-$(PFM)\$(VARIETY)\poolncv.exe: $(PFM)\$(VARIETY)\poolncv.obj \
+$(PFM)\$(VARIETY)\nailboardtest.exe: $(PFM)\$(VARIETY)\nailboardtest.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
+$(PFM)\$(VARIETY)\poolncv.exe: $(PFM)\$(VARIETY)\poolncv.obj \
+ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(POOLNOBJ)
+
$(PFM)\$(VARIETY)\qs.exe: $(PFM)\$(VARIETY)\qs.obj \
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
@@ -271,13 +346,13 @@ $(PFM)\$(VARIETY)\mpseventsql.obj: $(PFM)\$(VARIETY)\eventsql.obj
$(ECHO) $@
@if not exist $(PFM) mkdir $(PFM)
@if not exist $(PFM)\$(VARIETY) mkdir $(PFM)\$(VARIETY)
- cl /c $(CFLAGS) /Fd$(PFM)\$(VARIETY)\ /Fo$@ $<
+ $(CC) /c $(CFLAGS) /Fo$@ $<
$(PFM)\$(VARIETY)\sqlite3.obj:
$(ECHO) $@
@if not exist $(PFM) mkdir $(PFM)
@if not exist $(PFM)\$(VARIETY) mkdir $(PFM)\$(VARIETY)
- cl /c $(CFLAGSSQL) /Fd$(PFM)\$(VARIETY)\ /Fo$@ sqlite3.c
+ $(CC) /c $(CFLAGSSQL) /Fo$@ sqlite3.c
{}.asm{$(PFM)\$(VARIETY)}.obj:
$(ECHO) $@
@@ -299,12 +374,12 @@ $(PFM)\$(VARIETY)\sqlite3.obj:
{$(PFM)\$(VARIETY)}.obj{$(PFM)\$(VARIETY)}.exe:
$(ECHO) $@
- $(LINKER) $(LINKFLAGS) /PDB:$*.pdb /OUT:$@ $(**)
+ $(LINKER) $(LINKFLAGS) /OUT:$@ $(**)
# C. COPYRIGHT AND LICENSE
#
-# Copyright (C) 2001-2013 Ravenbrook Limited .
+# Copyright (C) 2001-2014 Ravenbrook Limited .
# All rights reserved. This is an open source license. Contact
# Ravenbrook for commercial licensing options.
#
diff --git a/code/commpre.nmk b/code/commpre.nmk
index a78eee3ef2..eaaa46c84a 100644
--- a/code/commpre.nmk
+++ b/code/commpre.nmk
@@ -1,7 +1,7 @@
# commpre.nmk: FIRST COMMON FRAGMENT FOR PLATFORMS USING NMAKE -*- makefile -*-1
#
# $Id$
-# Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
#
# DESCRIPTION
#
@@ -14,24 +14,29 @@
# %%PART: When adding a new part, add a new parameter for the files included
# in the part
# Parameters:
-# PFM platform code, e.g. "w3i3mv"
-# PFMDEFS /D options to define platforms preprocessor symbols
-# to the compiler. Eg "/DOS_NT /DARCH_386 /DBUILD_MVC"
+# PFM platform code, e.g. "w3i3mv"
+# PFMDEFS /D options to define platforms preprocessor symbols
+# to the compiler. Avoid using this if possible, as it
+# prevents the MPS being built with a simple command like
+# "cl mps.c".
# MPMCOMMON list of sources which make up the "mpm" part for all
-# platforms. Each source is stripped of its .c extension
-# and surrounded in angle brackets (<>)
-# MPM as above, plus sources for the "mpm" part for the current
-# platform.
-# PLINTH as above for the "plinth" part
-# AMC as above for the "amc" part
-# AMS as above for the "ams" part
-# LO as above for the "lo" part
-# POOLN as above for the "pooln" part
-# SNC as above for the "snc" part
-# DW as above for the "dw" part
-# FMTTEST as above for the "fmttest" part
-# TESTLIB as above for the "testlib" part
-# NOISY if defined, causes command to be emitted
+# platforms. Each source is stripped of its .c extension
+# and surrounded with [brackets].
+# MPMPF as above for the current platform.
+# PLINTH as above for the "plinth" part
+# AMC as above for the "amc" part
+# AMS as above for the "ams" part
+# LO as above for the "lo" part
+# POOLN as above for the "pooln" part
+# SNC as above for the "snc" part
+# POOLS as above for all pools included in the target
+# MPM as above for the MPMCOMMON + MPMPF + PLINTH + POOLS
+# DW as above for the "dw" part
+# FMTTEST as above for the "fmttest" part
+# FMTSCHEME as above for the "fmtscheme" part
+# TESTLIB as above for the "testlib" part
+# TESTTHR as above for the "testthr" part
+# NOISY if defined, causes command to be emitted
#
#
# EDITING
@@ -50,25 +55,51 @@
LIB_TARGETS=mps.lib
-# If it is suitable for running regularly (for example, after every
-# build) as an automated test case, add it to AUTO_TEST_TARGETS.
-
-AUTO_TEST_TARGETS=abqtest.exe amcss.exe amcsshe.exe amsss.exe \
- amssshe.exe apss.exe arenacv.exe awlut.exe awluthe.exe btcv.exe \
- exposet0.exe expt825.exe fbmtest.exe finalcv.exe finaltest.exe \
- fotest.exe locbwcss.exe lockcov.exe lockutw3.exe locusss.exe \
- locv.exe messtest.exe mpmss.exe mpsicv.exe mv2test.exe \
- poolncv.exe qs.exe sacss.exe segsmss.exe steptest.exe walkt0.exe \
+# Test cases go in TEST_TARGETS.
+
+TEST_TARGETS=\
+ abqtest.exe \
+ airtest.exe \
+ amcss.exe \
+ amcsshe.exe \
+ amcssth.exe \
+ amsss.exe \
+ amssshe.exe \
+ apss.exe \
+ arenacv.exe \
+ awlut.exe \
+ awluthe.exe \
+ awlutth.exe \
+ btcv.exe \
+ bttest.exe \
+ djbench.exe \
+ exposet0.exe \
+ expt825.exe \
+ finalcv.exe \
+ finaltest.exe \
+ fotest.exe \
+ gcbench.exe \
+ landtest.exe \
+ locbwcss.exe \
+ lockcov.exe \
+ lockut.exe \
+ locusss.exe \
+ locv.exe \
+ messtest.exe \
+ mpmss.exe \
+ mpsicv.exe \
+ mv2test.exe \
+ nailboardtest.exe \
+ poolncv.exe \
+ qs.exe \
+ sacss.exe \
+ segsmss.exe \
+ steptest.exe \
+ teletest.exe \
+ walkt0.exe \
+ zcoll.exe \
zmess.exe
-# If it is not runnable as an automated test case, but is buildable,
-# add it to OTHER_TEST_TARGETS with a note.
-#
-# bttest and teletest -- interactive and so cannot be run unattended.
-# zcoll -- takes too long to be useful as a regularly run smoke test.
-
-OTHER_TEST_TARGETS=bttest.exe teletest.exe zcoll.exe
-
# Stand-alone programs go in EXTRA_TARGETS if they should always be
# built, or in OPTIONAL_TARGETS if they should only be built if
@@ -82,7 +113,7 @@ OPTIONAL_TARGETS=mpseventsql.exe
UNBUILDABLE_TARGETS=replay.exe
-ALL_TARGETS=$(LIB_TARGETS) $(AUTO_TEST_TARGETS) $(OTHER_TEST_TARGETS) $(EXTRA_TARGETS)
+ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS)
# PARAMETERS
@@ -90,24 +121,72 @@ ALL_TARGETS=$(LIB_TARGETS) $(AUTO_TEST_TARGETS) $(OTHER_TEST_TARGETS) $(EXTRA_TA
#
# %%PART: When adding a new part, add the sources for the new part here.
-MPMCOMMON = \
- \
- \
- \
- [ \
- ] \
-
-PLINTH =
-AMC =
-AMS =
-AWL =
-LO =
-MVFF =
-POOLN =
-SNC =
-DW =
-FMTTEST =
-TESTLIB =
+MPMCOMMON=\
+ [abq] \
+ [arena] \
+ [arenacl] \
+ [arenavm] \
+ [arg] \
+ [boot] \
+ [bt] \
+ [buffer] \
+ [cbs] \
+ [dbgpool] \
+ [dbgpooli] \
+ [event] \
+ [failover] \
+ [format] \
+ [freelist] \
+ [global] \
+ [land] \
+ [ld] \
+ [locus] \
+ [message] \
+ [meter] \
+ [mpm] \
+ [mpsi] \
+ [nailboard] \
+ [pool] \
+ [poolabs] \
+ [poolmfs] \
+ [poolmrg] \
+ [poolmv2] \
+ [poolmv] \
+ [protocol] \
+ [range] \
+ [ref] \
+ [reserv] \
+ [ring] \
+ [root] \
+ [sa] \
+ [sac] \
+ [seg] \
+ [shield] \
+ [splay] \
+ [ss] \
+ [table] \
+ [trace] \
+ [traceanc] \
+ [tract] \
+ [tree] \
+ [version] \
+ [vm] \
+ [walk]
+PLINTH = [mpsliban] [mpsioan]
+AMC = [poolamc]
+AMS = [poolams] [poolamsi]
+AWL = [poolawl]
+LO = [poollo]
+MVFF = [poolmvff]
+POOLN = [pooln]
+SNC = [poolsnc]
+FMTDY = [fmtdy] [fmtno]
+FMTTEST = [fmthe] [fmtdy] [fmtno] [fmtdytst]
+FMTSCHEME = [fmtscheme]
+TESTLIB = [testlib] [getoptl]
+TESTTHR = [testthrw3]
+POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC)
+MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH)
# CHECK PARAMETERS
@@ -119,12 +198,15 @@ TESTLIB =
!IFNDEF PFM
!ERROR commpre.nmk: PFM not defined
!ENDIF
-!IFNDEF PFMDEFS
-!ERROR commpre.nmk: PFMDEFS not defined
+!IFNDEF MPM
+!ERROR commpre.nmk: MPM not defined
!ENDIF
!IFNDEF MPMCOMMON
!ERROR commpre.nmk: MPMCOMMON not defined
!ENDIF
+!IFNDEF MPMPF
+!ERROR commpre.nmk: MPMPF not defined
+!ENDIF
!IFNDEF PLINTH
!ERROR commpre.nmk: PLINTH not defined
!ENDIF
@@ -137,9 +219,27 @@ TESTLIB =
!IFNDEF AMS
!ERROR commpre.nmk: AMS not defined
!ENDIF
+!IFNDEF POOLN
+!ERROR commpre.nmk: POOLN not defined
+!ENDIF
+!IFNDEF SNC
+!ERROR commpre.nmk: SNC not defined
+!ENDIF
+!IFNDEF FMTDY
+!ERROR commpre.nmk: FMTDY not defined
+!ENDIF
+!IFNDEF FMTTEST
+!ERROR commpre.nmk: FMTTEST not defined
+!ENDIF
+!IFNDEF FMTSCHEME
+!ERROR commpre.nmk: FMTSCHEME not defined
+!ENDIF
!IFNDEF TESTLIB
!ERROR commpre.nmk: TESTLIB not defined
!ENDIF
+!IFNDEF TESTTHR
+!ERROR commpre.nmk: TESTTHR not defined
+!ENDIF
# DECLARATIONS
@@ -155,25 +255,20 @@ ECHO = echo
# C FLAGS
-# /MD means compile for multi-threaded environment with separate C library DLL.
-# /MT means compile for multi-threaded environment.
-# /ML means compile for single-threaded environment.
-# A 'd' at the end means compile for debugging.
-
CFLAGSTARGETPRE =
CFLAGSTARGETPOST =
-CRTFLAGSHOT = /MT
-CRTFLAGSCOOL = /MTd
-LINKFLAGSHOT = libcmt.lib
-LINKFLAGSCOOL = libcmtd.lib
+CRTFLAGSHOT =
+CRTFLAGSCOOL =
+LINKFLAGSHOT =
+LINKFLAGSCOOL =
CFLAGSSQLPRE = /nologo $(PFMDEFS)
-CFLAGSCOMMONPRE = /nologo /W4 /WX $(PFMDEFS) $(CFLAGSTARGETPRE)
+CFLAGSCOMMONPRE = /nologo $(PFMDEFS) $(CFLAGSTARGETPRE)
CFLAGSSQLPOST =
CFLAGSCOMMONPOST = $(CFLAGSTARGETPOST)
# Flags for use in the variety combinations
-CFLAGSHOT = /O2 /DNDEBUG
+CFLAGSHOT = /O2
# (above /O2 (maximise speed) used to be set to /Ox
# (maximise optimisations) in for tool versions before VS 9)
# We used to have /GZ here (stack probe).
@@ -183,7 +278,7 @@ CFLAGSHOT = /O2 /DNDEBUG
# building a DLL, mpsdy.dll, the linker step will fail (error LNK2001:
# unresolved external symbol __chkesp). See
# http://support.microsoft.com/kb/q191669/
-CFLAGSCOOL = /Od
+CFLAGSCOOL =
CFLAGSINTERNAL = /Zi
CFLAGSEXTERNAL =
@@ -216,7 +311,7 @@ LFCOOL = $(LINKFLAGSCOOL) $(LINKFLAGSINTERNAL)
# %%VARIETY: When adding a new variety, define a macro containing the flags
# for the new variety
LIBMAN = lib # can't call this LIB - it screws the environment
-LIBFLAGSCOMMON = /nologo
+LIBFLAGSCOMMON =
LIBFLAGSRASH =
LIBFLAGSHOT =
@@ -235,7 +330,7 @@ LIBFLAGSCOOL =
# C. COPYRIGHT AND LICENSE
#
-# Copyright (C) 2001-2013 Ravenbrook Limited .
+# Copyright (C) 2001-2014 Ravenbrook Limited .
# All rights reserved. This is an open source license. Contact
# Ravenbrook for commercial licensing options.
#
diff --git a/code/config.h b/code/config.h
index 4ad6ea51f1..11d5a943a0 100644
--- a/code/config.h
+++ b/code/config.h
@@ -1,7 +1,7 @@
/* config.h: MPS CONFIGURATION
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (c) 2002 Global Graphics Software.
*
* PURPOSE
@@ -147,11 +147,60 @@
* cc -O2 -c -DCONFIG_PLINTH_NONE mps.c
*/
-#if defined(CONFIG_PLINTH_NONE)
+#if !defined(CONFIG_PLINTH_NONE)
+#define PLINTH
+#else
#define PLINTH_NONE
#endif
+/* CONFIG_PF_ANSI -- use the ANSI platform
+ *
+ * This symbol tells mps.c to exclude the sources for the
+ * auto-detected platform, and use the generic ("ANSI") platform
+ * instead.
+ */
+
+#if defined(CONFIG_PF_ANSI)
+#define PLATFORM_ANSI
+#endif
+
+
+/* CONFIG_THREAD_SINGLE -- support single-threaded execution only
+ *
+ * This symbol causes the MPS to be built for single-threaded
+ * execution only, where locks are not needed and so lock operations
+ * can be defined as no-ops by lock.h.
+ */
+
+#if !defined(CONFIG_THREAD_SINGLE)
+#define LOCK
+#else
+#define LOCK_NONE
+#endif
+
+
+/* CONFIG_POLL_NONE -- no support for polling
+ *
+ * This symbol causes the MPS to built without support for polling.
+ * This means that garbage collections will only happen if requested
+ * explicitly via mps_arena_collect() or mps_arena_step(), but it also
+ * means that protection is not needed, and so shield operations can
+ * be replaced with no-ops in mpm.h.
+ */
+
+#if !defined(CONFIG_POLL_NONE)
+#define REMEMBERED_SET
+#define SHIELD
+#else
+#if !defined(CONFIG_THREAD_SINGLE)
+#error "CONFIG_POLL_NONE without CONFIG_THREAD_SINGLE"
+#endif
+#define REMEMBERED_SET_NONE
+#define SHIELD_NONE
+#endif
+
+
#define MPS_VARIETY_STRING \
MPS_ASSERT_STRING "." MPS_LOG_STRING "." MPS_STATS_STRING
@@ -160,55 +209,99 @@
#include "mpstd.h"
-/* Suppress Visual C warnings at warning level 4, */
-/* see mail.richard.1997-09-25.13-26. */
-/* Essentially the same settings are done in testlib.h. */
+/* Suppress Visual C warnings at /W4 (warning level 4) */
+/* This is also done in testlib.h. */
#ifdef MPS_BUILD_MV
-/* "unreferenced inline function has been removed" (windows.h) */
-#pragma warning(disable: 4514)
-
-/* "constant conditional" (MPS_END) */
+/* "constant conditional" (provoked by MPS_END) */
#pragma warning(disable: 4127)
-/* "unreachable code" (ASSERT, if cond is constantly true). */
-#pragma warning(disable: 4702)
+#endif /* MPS_BUILD_MV */
-/* "expression evaluates to a function which is missing an argument list" */
-#pragma warning(disable: 4550)
-/* "local variable is initialized but not referenced" */
-#pragma warning(disable: 4189)
+/* Suppress Pelles C warnings at /W2 (warning level 2) */
+/* Some of the same settings are done in testlib.h. */
-/* "not all control paths return a value" */
-#pragma warning(disable: 4715)
+#ifdef MPS_BUILD_PC
-/* MSVC 2.0 generates a warning when using NOCHECK or UNUSED */
-#ifdef _MSC_VER
-#if _MSC_VER < 1000
-#pragma warning(disable: 4705)
-#endif
-#else /* _MSC_VER */
-#error "Expected _MSC_VER to be defined for builder.mv"
-#endif /* _MSC_VER */
+/* "Unreachable code" (provoked by AVER, if condition is constantly true). */
+#pragma warn(disable: 2154)
+/* "Consider changing type to 'size_t' for loop variable" */
+#pragma warn(disable: 2804)
-/* Non-checking varieties give many spurious warnings because parameters
- * are suddenly unused, etc. We aren't interested in these
+#endif /* MPS_BUILD_PC */
+
+
+/* MPS_FILE -- expands to __FILE__ in nested macros */
+
+#ifdef MPS_BUILD_PC
+
+/* Pelles C loses definition of __FILE__ in deeply nested macro
+ * expansions. See
*/
+#define MPS_FILE "<__FILE__ unavailable in " MPS_PF_STRING ">"
+
+#else
-#if defined(AVER_AND_CHECK_NONE)
+#define MPS_FILE __FILE__
-/* "unreferenced formal parameter" */
-#pragma warning(disable: 4100)
+#endif
-/* "unreferenced local function has been removed" */
-#pragma warning(disable: 4505)
-#endif /* AVER_AND_CHECK_NONE */
+/* Function attributes */
+/* Some of these are also defined in testlib.h */
-#endif /* MPS_BUILD_MV */
+/* Attribute for functions that take a printf-like format argument, so
+ * that the compiler can check the format specifiers against the types
+ * of the arguments.
+ * GCC:
+ * Clang:
+ */
+#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL)
+#define ATTRIBUTE_FORMAT(ARGLIST) __attribute__((__format__ ARGLIST))
+#else
+#define ATTRIBUTE_FORMAT(ARGLIST)
+#endif
+
+/* Attribute for functions that should not be instrumented by Clang's
+ * address sanitizer.
+ *
+ */
+#if defined(MPS_BUILD_LL)
+#if __has_feature(address_sanitizer)
+#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((__no_sanitize_address__))
+#else
+#define ATTRIBUTE_NO_SANITIZE_ADDRESS
+#endif
+#else
+#define ATTRIBUTE_NO_SANITIZE_ADDRESS
+#endif
+
+/* Attribute for functions that do not return.
+ * GCC:
+ * Clang:
+ */
+#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL)
+#define ATTRIBUTE_NORETURN __attribute__((__noreturn__))
+#else
+#define ATTRIBUTE_NORETURN
+#endif
+
+/* Attribute for functions that may be unused in some build configurations.
+ * GCC:
+ *
+ * This attribute must be applied to all Check functions, otherwise
+ * the RASH variety fails to compile with -Wunused-function. (It
+ * should not be applied to functions that are unused in all build
+ * configurations: these functions should not be compiled.)
+ */
+#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL)
+#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+#else
+#define ATTRIBUTE_UNUSED
+#endif
/* EPVMDefaultSubsequentSegSIZE is a default for the alignment of
@@ -224,11 +317,6 @@
#define BUFFER_RANK_DEFAULT (mps_rank_exact())
-/* CBS Configuration -- see */
-
-#define CBS_EXTEND_BY_DEFAULT ((Size)4096)
-
-
/* Format defaults: see */
#define FMT_ALIGN_DEFAULT ((Align)MPS_PF_ALIGN)
@@ -241,15 +329,27 @@
#define FMT_CLASS_DEFAULT (&FormatDefaultClass)
+/* Pool AMC Configuration -- see */
+
+#define AMC_INTERIOR_DEFAULT TRUE
+/* AMC treats objects larger than or equal to this as "Large" */
+#define AMC_LARGE_SIZE_DEFAULT ((Size)32768)
+#define AMC_EXTEND_BY_DEFAULT ((Size)8192)
+
+
/* Pool AMS Configuration -- see */
-#define AMS_SUPPORT_AMBIGUOUS_DEFAULT FALSE
+#define AMS_SUPPORT_AMBIGUOUS_DEFAULT TRUE
#define AMS_GEN_DEFAULT 0
/* Pool AWL Configuration -- see */
#define AWL_GEN_DEFAULT 0
+#define AWL_HAVE_SEG_SA_LIMIT TRUE
+#define AWL_SEG_SA_LIMIT 200 /* TODO: Improve guesswork with measurements */
+#define AWL_HAVE_TOTAL_SA_LIMIT FALSE
+#define AWL_TOTAL_SA_LIMIT 0
/* Pool LO Configuration -- see */
@@ -259,6 +359,7 @@
/* Pool MV Configuration -- see */
+#define MV_ALIGN_DEFAULT MPS_PF_ALIGN
#define MV_EXTEND_BY_DEFAULT ((Size)65536)
#define MV_AVG_SIZE_DEFAULT ((Size)32)
#define MV_MAX_SIZE_DEFAULT ((Size)65536)
@@ -277,6 +378,7 @@
#define MVFF_SLOT_HIGH_DEFAULT FALSE
#define MVFF_ARENA_HIGH_DEFAULT FALSE
#define MVFF_FIRST_FIT_DEFAULT TRUE
+#define MVFF_SPARE_DEFAULT 0.75
/* Pool MVT Configuration -- see */
@@ -290,48 +392,81 @@
#define MVT_FRAG_LIMIT_DEFAULT 30
-/* Arena Configuration -- see
- *
- * .client.seg-size: ARENA_CLIENT_PAGE_SIZE is the size in bytes of a
- * "page" (i.e., segment granule) in the client arena. It's set at 8192
- * with no particular justification.
- */
+/* Arena Configuration -- see */
#define ArenaPollALLOCTIME (65536.0)
#define ARENA_ZONESHIFT ((Shift)20)
-#define ARENA_CLIENT_PAGE_SIZE ((Size)8192)
+/* .client.seg-size: ARENA_CLIENT_GRAIN_SIZE is the minimum size, in
+ * bytes, of a grain in the client arena. It's set at 8192 with no
+ * particular justification. */
+
+#define ARENA_CLIENT_GRAIN_SIZE ((Size)8192)
+
+#define ARENA_DEFAULT_COMMIT_LIMIT ((Size)-1)
+
+/* TODO: This should be proportional to the memory usage of the MPS, not
+ * a constant. That will require design, and then some interface and
+ * documenation changes. */
+#define ARENA_DEFAULT_SPARE_COMMIT_LIMIT ((Size)10uL*1024uL*1024uL)
+
+#define ARENA_DEFAULT_ZONED TRUE
+
+/* ARENA_MINIMUM_COLLECTABLE_SIZE is the minimum size (in bytes) of
+ * collectable memory that might be considered worthwhile to run a
+ * full garbage collection. */
+
+#define ARENA_MINIMUM_COLLECTABLE_SIZE ((Size)1000000)
+
+/* ARENA_DEFAULT_COLLECTION_RATE is an estimate of the MPS's
+ * collection rate (in bytes per second), for use in the case where
+ * there isn't enough data to use a measured value. */
+
+#define ARENA_DEFAULT_COLLECTION_RATE (25000000.0)
+
+/* ARENA_DEFAULT_COLLECTION_OVERHEAD is an estimate of the MPS's
+ * collection overhead (in seconds), for use in the case where there
+ * isn't enough data to use a measured value. */
+
+#define ARENA_DEFAULT_COLLECTION_OVERHEAD (0.1)
+
+/* ARENA_MAX_COLLECT_FRACTION is the maximum fraction of runtime that
+ * ArenaStep is prepared to spend in collections. */
+
+#define ARENA_MAX_COLLECT_FRACTION (0.1)
+
+/* ArenaDefaultZONESET is the zone set used by LocusPrefDEFAULT.
+ *
+ * TODO: This is left over from before branches 2014-01-29/mps-chain-zones
+ * and 2014-01-17/cbs-tract-alloc reformed allocation, and may now be
+ * doing more harm than good. Experiment with setting to ZoneSetUNIV. */
#define ArenaDefaultZONESET (ZoneSetUNIV << (MPS_WORD_WIDTH / 2))
-/* @@@@ knows the implementation of ZoneSets */
-
-/* .segpref.default: For EPcore, non-DL segments should be placed high */
-/* to reduce fragmentation of DL pools (see request.epcore.170193_). */
-/* .. _request.epcore.170193: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/epcore/170193 */
-#define SegPrefDEFAULT { \
- SegPrefSig, /* sig */ \
- TRUE, /* high */ \
+
+/* LocusPrefDEFAULT is the allocation preference used by manual pool
+ * classes (these don't care where they allocate). */
+
+#define LocusPrefDEFAULT { \
+ LocusPrefSig, /* sig */ \
+ FALSE, /* high */ \
ArenaDefaultZONESET, /* zoneSet */ \
- FALSE, /* isCollected */ \
+ ZoneSetEMPTY, /* avoid */ \
}
#define LDHistoryLENGTH ((Size)4)
-/* Value of MPS_KEY_EXTEND_BY for the arena control pool.
- Deliberately smaller than the default, because we don't expect the control
- pool to be very heavily used. */
-#define CONTROL_EXTEND_BY 4096
+/* Value of MPS_KEY_EXTEND_BY for the arena control pool. */
+#define CONTROL_EXTEND_BY ((Size)32768)
+#define VM_ARENA_SIZE_DEFAULT ((Size)1 << 28)
-/* Stack configuration */
-/* Currently StackProbe has a useful implementation only on
- * Intel platforms and only when using Microsoft build tools (builder.mv)
- */
-#if defined(MPS_ARCH_I3) && defined(MPS_BUILD_MV)
-#define StackProbeDEPTH ((Size)500)
-#elif defined(MPS_PF_W3I6MV)
+/* Stack configuration -- see */
+
+/* Currently StackProbe has a useful implementation only on Windows. */
+#if defined(MPS_OS_W3)
+/* See for a justification of this value. */
#define StackProbeDEPTH ((Size)500)
#else
#define StackProbeDEPTH ((Size)0)
@@ -346,7 +481,7 @@
/* VM Configuration -- see */
-#define VMANPageALIGNMENT ((Align)4096)
+#define VMAN_PAGE_SIZE ((Align)4096)
#define VMJunkBYTE ((unsigned char)0xA9)
#define VMParamSize (sizeof(Word))
@@ -358,6 +493,7 @@
*
* Source Symbols Header Feature
* =========== ========================= ============= ====================
+ * eventtxt.c setenv _GNU_SOURCE
* lockli.c pthread_mutexattr_settype _XOPEN_SOURCE >= 500
* prmci3li.c REG_EAX etc. _GNU_SOURCE
* prmci6li.c REG_RAX etc. _GNU_SOURCE
@@ -376,9 +512,14 @@
#if defined(MPS_OS_LI)
+#if defined(_XOPEN_SOURCE) && _XOPEN_SOURCE < 500
+#undef _XOPEN_SOURCE
+#endif
+#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 500
+#endif
-#ifndef _GNU_SOURCE
+#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
@@ -504,40 +645,18 @@
#define MPS_PROD_STRING "mps"
#define MPS_PROD_MPS
-#define THREAD_MULTI
-#define PROTECTION
-#define PROD_CHECKLEVEL_INITIAL CheckLevelSHALLOW
-
-/* TODO: This should be proportional to the memory usage of the MPS, not
- a constant. That will require design, and then some interface and
- documenation changes. */
-#define ARENA_INIT_SPARE_COMMIT_LIMIT ((Size)10uL*1024uL*1024uL)
-
-
-/* Pool Class AMC configuration */
-
-/* AMC treats segments of this many pages (or more) as "Large" */
-#define AMCLargeSegPAGES ((Count)8)
-
-
-/* Pool Class AWL configuration -- see poolawl.c for usage */
-
-#define AWL_HAVE_SEG_SA_LIMIT TRUE
-#define AWL_SEG_SA_LIMIT 200 /* TODO: Improve guesswork with measurements */
-#define AWL_HAVE_TOTAL_SA_LIMIT FALSE
-#define AWL_TOTAL_SA_LIMIT 0
/* Default chain for GC pools
*
* TODO: The default should be to measure liveness and make sensible
- * decisions.
+ * decisions. See job003794.
*/
#define ChainDEFAULT \
{ \
- { 8 * 1024, 0.85 }, /* 8MiB nursery */ \
- { 32 * 1024, 0.45 } /* 32MiB second gen, after which dynamic */ \
+ { 8 * 1024, 0.85 }, /* nursery */ \
+ { 36 * 1024, 0.45 } /* second gen, after which dynamic */ \
}
@@ -546,7 +665,7 @@
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/dbgpool.c b/code/dbgpool.c
index db4fab371c..6a8417595f 100644
--- a/code/dbgpool.c
+++ b/code/dbgpool.c
@@ -1,7 +1,7 @@
/* dbgpool.c: POOL DEBUG MIXIN
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (C) 2002 Global Graphics Software.
*
* .source: design.mps.object-debug
@@ -22,19 +22,18 @@ typedef struct tagStruct {
/* We don't want to pay the expense of a sig in every tag */
Addr addr;
Size size;
- SplayNodeStruct splayNode;
+ TreeStruct treeStruct;
char userdata[1 /* actually variable length */];
} tagStruct;
-#define SplayNode2Tag(node) PARENT(tagStruct, splayNode, (node))
+#define TagTree(tag) (&(tag)->treeStruct)
+#define TagOfTree(tree) TREE_ELT(tag, treeStruct, tree)
typedef tagStruct *Tag;
/* tag init methods: copying the user-supplied data into the tag */
-#define TagInitMethodCheck(f) FUNCHECK(f)
-
static void TagTrivInit(void* tag, va_list args)
{
UNUSED(tag); UNUSED(args);
@@ -43,22 +42,27 @@ static void TagTrivInit(void* tag, va_list args)
/* TagComp -- splay comparison function for address ordering of tags */
-static Compare TagComp(void *key, SplayNode node)
+static Compare TagCompare(Tree node, TreeKey key)
{
Addr addr1, addr2;
addr1 = *(Addr *)key;
- addr2 = SplayNode2Tag(node)->addr;
+ addr2 = TagOfTree(node)->addr;
if (addr1 < addr2)
return CompareLESS;
else if (addr1 > addr2) {
/* Check key is not inside the object of this tag */
- AVER_CRITICAL(AddrAdd(addr2, SplayNode2Tag(node)->size) <= addr1);
+ AVER_CRITICAL(AddrAdd(addr2, TagOfTree(node)->size) <= addr1);
return CompareGREATER;
} else
return CompareEQUAL;
}
+static TreeKey TagKey(Tree node)
+{
+ return &TagOfTree(node)->addr;
+}
+
/* PoolDebugMixinCheck -- check a PoolDebugMixin */
@@ -69,12 +73,12 @@ Bool PoolDebugMixinCheck(PoolDebugMixin debug)
/* Nothing to check about freeTemplate */
/* Nothing to check about freeSize */
if (debug->tagInit != NULL) {
- CHECKL(TagInitMethodCheck(debug->tagInit));
+ CHECKL(FUNCHECK(debug->tagInit));
/* Nothing to check about tagSize */
CHECKD(Pool, debug->tagPool);
CHECKL(COMPATTYPE(Addr, void*)); /* tagPool relies on this */
/* Nothing to check about missingTags */
- CHECKL(SplayTreeCheck(&debug->index));
+ CHECKD(SplayTree, &debug->index);
}
UNUSED(debug); /* see */
return TRUE;
@@ -117,23 +121,25 @@ Bool PoolDebugOptionsCheck(PoolDebugOptions opt)
* Someday, this could be split into fence and tag init methods.
*/
-ARG_DEFINE_KEY(pool_debug_options, PoolDebugOptions);
+ARG_DEFINE_KEY(POOL_DEBUG_OPTIONS, PoolDebugOptions);
+
+static PoolDebugOptionsStruct debugPoolOptionsDefault = {
+ "POST", 4, "DEAD", 4,
+};
static Res DebugPoolInit(Pool pool, ArgList args)
{
Res res;
- PoolDebugOptions options;
+ PoolDebugOptions options = &debugPoolOptionsDefault;
PoolDebugMixin debug;
- TagInitMethod tagInit;
+ TagInitFunction tagInit;
Size tagSize;
ArgStruct arg;
AVERT(Pool, pool);
- /* TODO: Split this structure into separate keyword arguments,
- now that we can support them. */
- ArgRequire(&arg, args, MPS_KEY_POOL_DEBUG_OPTIONS);
- options = (PoolDebugOptions)arg.val.pool_debug_options;
+ if (ArgPick(&arg, args, MPS_KEY_POOL_DEBUG_OPTIONS))
+ options = (PoolDebugOptions)arg.val.pool_debug_options;
AVERT(PoolDebugOptions, options);
@@ -154,10 +160,6 @@ static Res DebugPoolInit(Pool pool, ArgList args)
/* into Addr memory, to avoid breaking . */
debug->fenceSize = options->fenceSize;
if (debug->fenceSize != 0) {
- if (debug->fenceSize % PoolAlignment(pool) != 0) {
- res = ResPARAM;
- goto alignFail;
- }
/* Fenceposting turns on tagging */
if (tagInit == NULL) {
tagSize = 0;
@@ -172,10 +174,6 @@ static Res DebugPoolInit(Pool pool, ArgList args)
/* into Addr memory, to avoid breaking . */
debug->freeSize = options->freeSize;
if (debug->freeSize != 0) {
- if (PoolAlignment(pool) % debug->freeSize != 0) {
- res = ResPARAM;
- goto alignFail;
- }
debug->freeTemplate = options->freeTemplate;
}
@@ -186,14 +184,17 @@ static Res DebugPoolInit(Pool pool, ArgList args)
/* This pool has to be like the arena control pool: the blocks */
/* allocated must be accessible using void*. */
MPS_ARGS_BEGIN(pcArgs) {
- MPS_ARGS_ADD(pcArgs, MPS_KEY_EXTEND_BY, debug->tagSize); /* FIXME: Check this */
+ /* By setting EXTEND_BY to debug->tagSize we get the smallest
+ possible extensions compatible with the tags, and so the
+ least amount of wasted space. */
+ MPS_ARGS_ADD(pcArgs, MPS_KEY_EXTEND_BY, debug->tagSize);
MPS_ARGS_ADD(pcArgs, MPS_KEY_MFS_UNIT_SIZE, debug->tagSize);
res = PoolCreate(&debug->tagPool, PoolArena(pool), PoolClassMFS(), pcArgs);
} MPS_ARGS_END(pcArgs);
if (res != ResOK)
goto tagFail;
debug->missingTags = 0;
- SplayTreeInit(&debug->index, TagComp, NULL);
+ SplayTreeInit(&debug->index, TagCompare, TagKey, SplayTrivUpdate);
}
debug->sig = PoolDebugMixinSig;
@@ -201,7 +202,6 @@ static Res DebugPoolInit(Pool pool, ArgList args)
return ResOK;
tagFail:
-alignFail:
SuperclassOfPool(pool)->finish(pool);
AVER(res != ResOK);
return res;
@@ -227,39 +227,151 @@ static void DebugPoolFinish(Pool pool)
}
-/* freeSplat -- splat free block with splat pattern
+/* patternIterate -- call visitor for occurrences of pattern between
+ * base and limit
+ *
+ * pattern is an arbitrary pattern that's size bytes long.
*
- * If base is in a segment, the whole block has to be in it.
+ * Imagine that the entirety of memory were covered by contiguous
+ * copies of pattern starting at address 0. Then call visitor for each
+ * copy (or part) of pattern that lies between base and limit. In each
+ * call, target is the address of the copy or part (where base <=
+ * target < limit); source is the corresponding byte of the pattern
+ * (where pattern <= source < pattern + size); and size is the length
+ * of the copy or part.
*/
+typedef Bool (*patternVisitor)(Addr target, ReadonlyAddr source, Size size);
+
+static Bool patternIterate(ReadonlyAddr pattern, Size size,
+ Addr base, Addr limit, patternVisitor visitor)
+{
+ Addr p;
+
+ AVER(pattern != NULL);
+ AVER(0 < size);
+ AVER(base != NULL);
+ AVER(base <= limit);
+
+ p = base;
+ while (p < limit) {
+ Addr end = AddrAdd(p, size);
+ Addr rounded = AddrRoundUp(p, size);
+ Size offset = (Word)p % size;
+ if (end < p || rounded < p) {
+ /* Address range overflow */
+ break;
+ } else if (p == rounded && end <= limit) {
+ /* Room for a whole copy */
+ if (!(*visitor)(p, pattern, size))
+ return FALSE;
+ p = end;
+ } else if (p < rounded && rounded <= end && rounded <= limit) {
+ /* Copy up to rounded */
+ if (!(*visitor)(p, ReadonlyAddrAdd(pattern, offset),
+ AddrOffset(p, rounded)))
+ return FALSE;
+ p = rounded;
+ } else {
+ /* Copy up to limit */
+ AVER(limit <= end);
+ AVER(p == rounded || limit <= rounded);
+ if (!(*visitor)(p, ReadonlyAddrAdd(pattern, offset),
+ AddrOffset(p, limit)))
+ return FALSE;
+ p = limit;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/* patternCopy -- copy pattern to fill a range
+ *
+ * Fill the range of addresses from base (inclusive) to limit
+ * (exclusive) with copies of pattern (which is size bytes long).
+ */
+
+static Bool patternCopyVisitor(Addr target, ReadonlyAddr source, Size size)
+{
+ (void)AddrCopy(target, source, size);
+ return TRUE;
+}
+
+static void patternCopy(ReadonlyAddr pattern, Size size, Addr base, Addr limit)
+{
+ (void)patternIterate(pattern, size, base, limit, patternCopyVisitor);
+}
+
+
+/* patternCheck -- check pattern against a range
+ *
+ * Compare the range of addresses from base (inclusive) to limit
+ * (exclusive) with copies of pattern (which is size bytes long). The
+ * copies of pattern must be arranged so that fresh copies start at
+ * aligned addresses wherever possible.
+ */
+
+static Bool patternCheckVisitor(Addr target, ReadonlyAddr source, Size size)
+{
+ return AddrComp(target, source, size) == 0;
+}
+
+static Bool patternCheck(ReadonlyAddr pattern, Size size, Addr base, Addr limit)
+{
+ return patternIterate(pattern, size, base, limit, patternCheckVisitor);
+}
+
+
+/* debugPoolSegIterate -- iterate over a range of segments in an arena
+ *
+ * Expects to be called on a range corresponding to objects withing a
+ * single pool.
+ *
+ * NOTE: This relies on pools consistently using segments
+ * contiguously.
+ */
+
+static void debugPoolSegIterate(Arena arena, Addr base, Addr limit,
+ void (*visitor)(Arena, Seg))
+{
+ Seg seg;
+
+ if (SegOfAddr(&seg, arena, base)) {
+ do {
+ base = SegLimit(seg);
+ (*visitor)(arena, seg);
+ } while (base < limit && SegOfAddr(&seg, arena, base));
+ AVER(base >= limit); /* shouldn't run out of segments */
+ }
+}
+
+static void debugPoolShieldExpose(Arena arena, Seg seg)
+{
+ ShieldExpose(arena, seg);
+}
+
+static void debugPoolShieldCover(Arena arena, Seg seg)
+{
+ ShieldCover(arena, seg);
+}
+
+
+/* freeSplat -- splat free block with splat pattern */
+
static void freeSplat(PoolDebugMixin debug, Pool pool, Addr base, Addr limit)
{
- Addr p, next;
- Size freeSize = debug->freeSize;
Arena arena;
- Seg seg = NULL; /* suppress "may be used uninitialized" */
- Bool inSeg;
AVER(base < limit);
- /* If the block is in a segment, make sure any shield is up. */
+ /* If the block is in one or more segments, make sure the segments
+ are exposed so that we can overwrite the block with the pattern. */
arena = PoolArena(pool);
- inSeg = SegOfAddr(&seg, arena, base);
- if (inSeg) {
- AVER(limit <= SegLimit(seg));
- ShieldExpose(arena, seg);
- }
- /* Write as many copies of the template as fit in the block. */
- for (p = base, next = AddrAdd(p, freeSize);
- next <= limit && p < next /* watch out for overflow in next */;
- p = next, next = AddrAdd(next, freeSize))
- (void)AddrCopy(p, debug->freeTemplate, freeSize);
- /* Fill the tail of the block with a partial copy of the template. */
- if (next > limit || next < p)
- (void)AddrCopy(p, debug->freeTemplate, AddrOffset(p, limit));
- if (inSeg) {
- ShieldCover(arena, seg);
- }
+ debugPoolSegIterate(arena, base, limit, debugPoolShieldExpose);
+ patternCopy(debug->freeTemplate, debug->freeSize, base, limit);
+ debugPoolSegIterate(arena, base, limit, debugPoolShieldCover);
}
@@ -267,41 +379,17 @@ static void freeSplat(PoolDebugMixin debug, Pool pool, Addr base, Addr limit)
static Bool freeCheck(PoolDebugMixin debug, Pool pool, Addr base, Addr limit)
{
- Addr p, next;
- Size freeSize = debug->freeSize;
- Res res;
+ Bool res;
Arena arena;
- Seg seg = NULL; /* suppress "may be used uninitialized" */
- Bool inSeg;
AVER(base < limit);
- /* If the block is in a segment, make sure any shield is up. */
+ /* If the block is in one or more segments, make sure the segments
+ are exposed so we can read the pattern. */
arena = PoolArena(pool);
- inSeg = SegOfAddr(&seg, arena, base);
- if (inSeg) {
- AVER(limit <= SegLimit(seg));
- ShieldExpose(arena, seg);
- }
- /* Compare this to the AddrCopys in freeSplat. */
- /* Check the complete copies of the template in the block. */
- for (p = base, next = AddrAdd(p, freeSize);
- next <= limit && p < next /* watch out for overflow in next */;
- p = next, next = AddrAdd(next, freeSize))
- if (AddrComp(p, debug->freeTemplate, freeSize) != 0) {
- res = FALSE; goto done;
- }
- /* Check the partial copy of the template at the tail of the block. */
- if (next > limit || next < p)
- if (AddrComp(p, debug->freeTemplate, AddrOffset(p, limit)) != 0) {
- res = FALSE; goto done;
- }
- res = TRUE;
-
-done:
- if (inSeg) {
- ShieldCover(arena, seg);
- }
+ debugPoolSegIterate(arena, base, limit, debugPoolShieldExpose);
+ res = patternCheck(debug->freeTemplate, debug->freeSize, base, limit);
+ debugPoolSegIterate(arena, base, limit, debugPoolShieldCover);
return res;
}
@@ -347,63 +435,75 @@ static void freeCheckFree(PoolDebugMixin debug,
* start fp client object slop end fp
*
* slop is the extra allocation from rounding up the client request to
- * the pool's alignment. The fenceposting code does this, so there's a
- * better chance of the end fencepost being flush with the next object
- * (can't be guaranteed, since the underlying pool could have allocated
- * an even larger block). The alignment slop is filled from the
- * fencepost template as well (as much as fits, .fence.size guarantees
- * the template is larger).
+ * the pool's alignment. The fenceposting code adds this slop so that
+ * there's a better chance of the end fencepost being flush with the
+ * next object (though it can't be guaranteed, since the underlying
+ * pool could have allocated an even larger block). The alignment slop
+ * is filled from the fencepost template as well.
+ *
+ * Keep in sync with fenceCheck.
*/
static Res fenceAlloc(Addr *aReturn, PoolDebugMixin debug, Pool pool,
Size size, Bool withReservoir)
{
Res res;
- Addr new, clientNew;
- Size alignedSize;
+ Addr obj, startFence, clientNew, clientLimit, limit;
+ Size alignedFenceSize, alignedSize;
AVER(aReturn != NULL);
+ AVERT(PoolDebugMixin, debug);
+ AVERT(Pool, pool);
+ alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool));
alignedSize = SizeAlignUp(size, PoolAlignment(pool));
- res = freeCheckAlloc(&new, debug, pool, alignedSize + 2*debug->fenceSize,
+ res = freeCheckAlloc(&obj, debug, pool,
+ alignedSize + 2 * alignedFenceSize,
withReservoir);
if (res != ResOK)
return res;
- clientNew = AddrAdd(new, debug->fenceSize);
+
+ startFence = obj;
+ clientNew = AddrAdd(startFence, alignedFenceSize);
+ clientLimit = AddrAdd(clientNew, size);
+ limit = AddrAdd(clientNew, alignedSize + alignedFenceSize);
+
/* @@@@ shields? */
- /* start fencepost */
- (void)AddrCopy(new, debug->fenceTemplate, debug->fenceSize);
- /* alignment slop */
- (void)AddrCopy(AddrAdd(clientNew, size),
- debug->fenceTemplate, alignedSize - size);
- /* end fencepost */
- (void)AddrCopy(AddrAdd(clientNew, alignedSize),
- debug->fenceTemplate, debug->fenceSize);
+ patternCopy(debug->fenceTemplate, debug->fenceSize, startFence, clientNew);
+ patternCopy(debug->fenceTemplate, debug->fenceSize, clientLimit, limit);
*aReturn = clientNew;
- return res;
+ return ResOK;
}
-/* fenceCheck -- check fences of an object */
+/* fenceCheck -- check fences of an object
+ *
+ * Keep in sync with fenceAlloc.
+ */
static Bool fenceCheck(PoolDebugMixin debug, Pool pool, Addr obj, Size size)
{
- Size alignedSize;
+ Addr startFence, clientNew, clientLimit, limit;
+ Size alignedFenceSize, alignedSize;
AVERT_CRITICAL(PoolDebugMixin, debug);
AVERT_CRITICAL(Pool, pool);
/* Can't check obj */
+ alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool));
alignedSize = SizeAlignUp(size, PoolAlignment(pool));
+
+ startFence = AddrSub(obj, alignedFenceSize);
+ clientNew = obj;
+ clientLimit = AddrAdd(clientNew, size);
+ limit = AddrAdd(clientNew, alignedSize + alignedFenceSize);
+
/* @@@@ shields? */
- /* Compare this to the AddrCopys in fenceAlloc */
- return (AddrComp(AddrSub(obj, debug->fenceSize), debug->fenceTemplate,
- debug->fenceSize) == 0
- && AddrComp(AddrAdd(obj, size), debug->fenceTemplate,
- alignedSize - size) == 0
- && AddrComp(AddrAdd(obj, alignedSize), debug->fenceTemplate,
- debug->fenceSize) == 0);
+ return patternCheck(debug->fenceTemplate, debug->fenceSize,
+ startFence, clientNew)
+ && patternCheck(debug->fenceTemplate, debug->fenceSize,
+ clientLimit, limit);
}
@@ -412,13 +512,14 @@ static Bool fenceCheck(PoolDebugMixin debug, Pool pool, Addr obj, Size size)
static void fenceFree(PoolDebugMixin debug,
Pool pool, Addr old, Size size)
{
- Size alignedSize;
+ Size alignedFenceSize, alignedSize;
ASSERT(fenceCheck(debug, pool, old, size), "fencepost check on free");
+ alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool));
alignedSize = SizeAlignUp(size, PoolAlignment(pool));
- freeCheckFree(debug, pool, AddrSub(old, debug->fenceSize),
- alignedSize + 2*debug->fenceSize);
+ freeCheckFree(debug, pool, AddrSub(old, alignedFenceSize),
+ alignedSize + 2 * alignedFenceSize);
}
@@ -429,6 +530,7 @@ static Res tagAlloc(PoolDebugMixin debug,
{
Tag tag;
Res res;
+ Bool b;
Addr addr;
UNUSED(pool);
@@ -443,10 +545,10 @@ static Res tagAlloc(PoolDebugMixin debug,
}
tag = (Tag)addr;
tag->addr = new; tag->size = size;
- SplayNodeInit(&tag->splayNode);
+ TreeInit(TagTree(tag));
/* In the future, we might call debug->tagInit here. */
- res = SplayTreeInsert(&debug->index, &tag->splayNode, (void *)&new);
- AVER(res == ResOK);
+ b = SplayTreeInsert(&debug->index, TagTree(tag));
+ AVER(b);
return ResOK;
}
@@ -455,25 +557,25 @@ static Res tagAlloc(PoolDebugMixin debug,
static void tagFree(PoolDebugMixin debug, Pool pool, Addr old, Size size)
{
- SplayNode node;
+ Tree node;
Tag tag;
- Res res;
+ Bool b;
AVERT(PoolDebugMixin, debug);
AVERT(Pool, pool);
AVER(size > 0);
- res = SplayTreeSearch(&node, &debug->index, (void *)&old);
- if (res != ResOK) {
+ if (!SplayTreeFind(&node, &debug->index, &old)) {
AVER(debug->missingTags > 0);
debug->missingTags--;
return;
}
- tag = SplayNode2Tag(node);
+ tag = TagOfTree(node);
AVER(tag->size == size);
- res = SplayTreeDelete(&debug->index, node, (void *)&old);
- AVER(res == ResOK);
- SplayNodeFinish(node);
+ AVER(tag->addr == old);
+ b = SplayTreeDelete(&debug->index, node);
+ AVER(b); /* expect tag to be in the tree */
+ TreeFinish(node);
PoolFree(debug->tagPool, (Addr)tag, debug->tagSize);
}
@@ -546,33 +648,28 @@ static void DebugPoolFree(Pool pool, Addr old, Size size)
/* TagWalk -- walk all objects in the pool using tags */
-typedef void (*ObjectsStepMethod)(Addr addr, Size size, Format fmt,
- Pool pool, void *tagData, void *p);
-
-#define ObjectsStepMethodCheck(f) \
- ((f) != NULL) /* that's the best we can do */
+typedef void (*ObjectsVisitor)(Addr addr, Size size, Format fmt,
+ Pool pool, void *tagData, void *p);
-static void TagWalk(Pool pool, ObjectsStepMethod step, void *p)
+static void TagWalk(Pool pool, ObjectsVisitor visitor, void *p)
{
- SplayNode node;
+ Tree node;
PoolDebugMixin debug;
- Addr dummy = NULL; /* Breaks , but it's */
- /* only temporary until SplayTreeFirst is fixed. */
AVERT(Pool, pool);
- AVERT(ObjectsStepMethod, step);
+ AVER(FUNCHECK(visitor));
/* Can't check p */
debug = DebugPoolDebugMixin(pool);
AVER(debug != NULL);
AVERT(PoolDebugMixin, debug);
- node = SplayTreeFirst(&debug->index, (void *)&dummy);
- while (node != NULL) {
- Tag tag = SplayNode2Tag(node);
+ node = SplayTreeFirst(&debug->index);
+ while (node != TreeEMPTY) {
+ Tag tag = TagOfTree(node);
- step(tag->addr, tag->size, NULL, pool, &tag->userdata, p);
- node = SplayTreeNext(&debug->index, node, (void *)&tag->addr);
+ (*visitor)(tag->addr, tag->size, NULL, pool, &tag->userdata, p);
+ node = SplayTreeNext(&debug->index, &tag->addr);
}
}
@@ -686,7 +783,7 @@ void PoolClassMixInDebug(PoolClass class)
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/dbgpool.h b/code/dbgpool.h
index bacecb1125..e01d8c3b65 100644
--- a/code/dbgpool.h
+++ b/code/dbgpool.h
@@ -3,7 +3,7 @@
* See .
*
* $Id$
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (C) 2002 Global Graphics Software.
*/
@@ -15,9 +15,9 @@
#include
-/* tag init methods: copying the user-supplied data into the tag */
+/* tag init function: copies the user-supplied data into the tag */
-typedef void (*TagInitMethod)(void* tag, va_list args);
+typedef void (*TagInitFunction)(void *tag, va_list args);
/* PoolDebugOptions -- option structure for debug pool init
@@ -26,11 +26,11 @@ typedef void (*TagInitMethod)(void* tag, va_list args);
*/
typedef struct PoolDebugOptionsStruct {
- void* fenceTemplate;
+ const void *fenceTemplate;
Size fenceSize;
- void* freeTemplate;
+ const void *freeTemplate;
Size freeSize;
- /* TagInitMethod tagInit; */
+ /* TagInitFunction tagInit; */
/* Size tagSize; */
} PoolDebugOptionsStruct;
@@ -43,11 +43,11 @@ typedef PoolDebugOptionsStruct *PoolDebugOptions;
typedef struct PoolDebugMixinStruct {
Sig sig;
- Addr fenceTemplate;
+ const struct AddrStruct *fenceTemplate;
Size fenceSize;
- Addr freeTemplate;
+ const struct AddrStruct *freeTemplate;
Size freeSize;
- TagInitMethod tagInit;
+ TagInitFunction tagInit;
Size tagSize;
Pool tagPool;
Count missingTags;
@@ -73,7 +73,7 @@ extern void DebugPoolFreeCheck(Pool pool, Addr base, Addr limit);
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/djbench.c b/code/djbench.c
index 09eaa6d513..fbc6dcabc1 100644
--- a/code/djbench.c
+++ b/code/djbench.c
@@ -13,14 +13,18 @@
#include "mps.c"
-#include
-#include
-#include
-#include
-#include
-#include "getopt.h"
#include "testlib.h"
+#include "testthr.h"
+
+#ifdef MPS_OS_W3
+#include "getopt.h"
+#else
+#include
+#endif
+#include /* fprintf, stderr */
+#include /* alloca, exit, EXIT_SUCCESS, EXIT_FAILURE */
+#include /* CLOCKS_PER_SEC, clock */
#define DJMUST(expr) \
do { \
@@ -48,6 +52,9 @@ static unsigned sshift = 18; /* log2 max block size in words */
static double pact = 0.2; /* probability per pass of acting */
static unsigned rinter = 75; /* pass interval for recursion */
static unsigned rmax = 10; /* maximum recursion depth */
+static mps_bool_t zoned = TRUE; /* arena allocates using zones */
+static size_t arena_size = 256ul * 1024 * 1024; /* arena size */
+static size_t arena_grain_size = 1; /* arena grain size */
#define DJRUN(fname, alloc, free) \
static unsigned fname##_inner(mps_ap_t ap, unsigned depth, unsigned r) { \
@@ -56,6 +63,7 @@ static unsigned rmax = 10; /* maximum recursion depth */
\
for (k = 0; k < nblocks; ++k) { \
blocks[k].p = NULL; \
+ blocks[k].s = 0; \
} \
\
for (j = 0; j < npass; ++j) { \
@@ -64,7 +72,8 @@ static unsigned rmax = 10; /* maximum recursion depth */
if (blocks[k].p == NULL) { \
size_t s = rnd() % ((sizeof(void *) << (rnd() % sshift)) - 1); \
void *p = NULL; \
- if (s > 0) alloc(p, s); \
+ if (s > 0) \
+ alloc(p, s); \
blocks[k].p = p; \
blocks[k].s = s; \
} else { \
@@ -124,8 +133,8 @@ DJRUN(dj_alloc, MPS_ALLOC, MPS_FREE)
#define RESERVE_ALLOC(p, s) \
do { \
size_t _s = ALIGN_UP(s, (size_t)MPS_PF_ALIGN); \
- mps_reserve(&p, ap, _s); \
- mps_commit(ap, p, _s); \
+ (void)mps_reserve(&p, ap, _s); \
+ (void)mps_commit(ap, p, _s); \
} while(0)
#define RESERVE_FREE(p, s) do { mps_free(pool, p, s); } while(0)
@@ -135,24 +144,14 @@ typedef void *(*dj_t)(void *);
static void weave(dj_t dj)
{
- pthread_t *threads = alloca(sizeof(threads[0]) * nthreads);
+ testthr_t *threads = alloca(sizeof(threads[0]) * nthreads);
unsigned t;
- for (t = 0; t < nthreads; ++t) {
- int err = pthread_create(&threads[t], NULL, dj, NULL);
- if (err != 0) {
- fprintf(stderr, "Unable to create thread: %d\n", err);
- exit(EXIT_FAILURE);
- }
- }
+ for (t = 0; t < nthreads; ++t)
+ testthr_create(&threads[t], dj, NULL);
- for (t = 0; t < nthreads; ++t) {
- int err = pthread_join(threads[t], NULL);
- if (err != 0) {
- fprintf(stderr, "Unable to join thread: %d\n", err);
- exit(EXIT_FAILURE);
- }
- }
+ for (t = 0; t < nthreads; ++t)
+ testthr_join(&threads[t], NULL);
}
@@ -173,7 +172,7 @@ static void watch(dj_t dj, const char *name)
/* Wrap a call to dj benchmark that doesn't require MPS setup */
-static void wrap(dj_t dj, mps_class_t dummy, const char *name)
+static void wrap(dj_t dj, mps_pool_class_t dummy, const char *name)
{
(void)dummy;
pool = NULL;
@@ -183,10 +182,12 @@ static void wrap(dj_t dj, mps_class_t dummy, const char *name)
/* Wrap a call to a dj benchmark that requires MPS setup */
-static void arena_wrap(dj_t dj, mps_class_t pool_class, const char *name)
+static void arena_wrap(dj_t dj, mps_pool_class_t pool_class, const char *name)
{
MPS_ARGS_BEGIN(args) {
- MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 256ul * 1024 * 1024); /* FIXME: Why is there no default? */
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arena_size);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size);
+ MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, zoned);
DJMUST(mps_arena_create_k(&arena, mps_arena_class_vm(), args));
} MPS_ARGS_END(args);
DJMUST(mps_pool_create_k(&pool, arena, pool_class, mps_args_none));
@@ -199,32 +200,35 @@ static void arena_wrap(dj_t dj, mps_class_t pool_class, const char *name)
/* Command-line options definitions. See getopt_long(3). */
static struct option longopts[] = {
- {"help", no_argument, NULL, 'h'},
- {"nthreads",required_argument, NULL, 't'},
- {"niter", required_argument, NULL, 'i'},
- {"npass", required_argument, NULL, 'p'},
- {"nblocks", required_argument, NULL, 'b'},
- {"sshift", required_argument, NULL, 's'},
- {"pact", required_argument, NULL, 'a'},
- {"rinter", required_argument, NULL, 'r'},
- {"rmax", required_argument, NULL, 'd'},
- {"seed", required_argument, NULL, 'x'},
- {NULL, 0, NULL, 0}
+ {"help", no_argument, NULL, 'h'},
+ {"nthreads", required_argument, NULL, 't'},
+ {"niter", required_argument, NULL, 'i'},
+ {"npass", required_argument, NULL, 'p'},
+ {"nblocks", required_argument, NULL, 'b'},
+ {"sshift", required_argument, NULL, 's'},
+ {"pact", required_argument, NULL, 'c'},
+ {"rinter", required_argument, NULL, 'r'},
+ {"rmax", required_argument, NULL, 'd'},
+ {"seed", required_argument, NULL, 'x'},
+ {"arena-size", required_argument, NULL, 'm'},
+ {"arena-grain-size", required_argument, NULL, 'a'},
+ {"arena-unzoned", no_argument, NULL, 'z'},
+ {NULL, 0, NULL, 0 }
};
/* Test definitions. */
-static mps_class_t dummy_class(void)
+static mps_pool_class_t dummy_class(void)
{
return NULL;
}
static struct {
const char *name;
- void (*wrap)(dj_t, mps_class_t, const char *name);
+ void (*wrap)(dj_t, mps_pool_class_t, const char *name);
dj_t dj;
- mps_class_t (*pool_class)(void);
+ mps_pool_class_t (*pool_class)(void);
} pools[] = {
{"mvt", arena_wrap, dj_reserve, mps_class_mvt},
{"mvff", arena_wrap, dj_reserve, mps_class_mvff},
@@ -242,7 +246,7 @@ int main(int argc, char *argv[]) {
seed = rnd_seed();
- while ((ch = getopt_long(argc, argv, "ht:i:p:b:s:a:r:d:x:", longopts, NULL)) != -1)
+ while ((ch = getopt_long(argc, argv, "ht:i:p:b:s:c:r:d:m:a:x:z", longopts, NULL)) != -1)
switch (ch) {
case 't':
nthreads = (unsigned)strtoul(optarg, NULL, 10);
@@ -259,7 +263,7 @@ int main(int argc, char *argv[]) {
case 's':
sshift = (unsigned)strtoul(optarg, NULL, 10);
break;
- case 'a':
+ case 'c':
pact = strtod(optarg, NULL);
break;
case 'r':
@@ -271,10 +275,47 @@ int main(int argc, char *argv[]) {
case 'x':
seed = strtoul(optarg, NULL, 10);
break;
+ case 'z':
+ zoned = FALSE;
+ break;
+ case 'm': {
+ char *p;
+ arena_size = (unsigned)strtoul(optarg, &p, 10);
+ switch(toupper(*p)) {
+ case 'G': arena_size <<= 30; break;
+ case 'M': arena_size <<= 20; break;
+ case 'K': arena_size <<= 10; break;
+ case '\0': break;
+ default:
+ fprintf(stderr, "Bad arena size %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ }
+ break;
+ case 'a': {
+ char *p;
+ arena_grain_size = (unsigned)strtoul(optarg, &p, 10);
+ switch(toupper(*p)) {
+ case 'G': arena_grain_size <<= 30; break;
+ case 'M': arena_grain_size <<= 20; break;
+ case 'K': arena_grain_size <<= 10; break;
+ case '\0': break;
+ default:
+ fprintf(stderr, "Bad arena grain size %s\n", optarg);
+ return EXIT_FAILURE;
+ }
+ }
+ break;
default:
+ /* This is printed in parts to keep within the 509 character
+ limit for string literals in portable standard C. */
fprintf(stderr,
"Usage: %s [option...] [test...]\n"
"Options:\n"
+ " -m n, --arena-size=n[KMG]?\n"
+ " Initial size of arena (default %lu).\n"
+ " -g n, --arena-grain-size=n[KMG]?\n"
+ " Arena grain size (default %lu).\n"
" -t n, --nthreads=n\n"
" Launch n threads each running the test\n"
" -i n, --niter=n\n"
@@ -284,28 +325,32 @@ int main(int argc, char *argv[]) {
" -b n, --nblocks=n\n"
" Length of the block array (default %u).\n"
" -s n, --sshift=n\n"
- " Log2 max block size in words (default %u).\n"
- " -a p, --pact=p\n"
- " Probability of acting on a block (default %g).\n",
+ " Log2 max block size in words (default %u).\n",
argv[0],
+ (unsigned long)arena_size,
+ (unsigned long)arena_grain_size,
niter,
npass,
nblocks,
- sshift,
- pact);
+ sshift);
fprintf(stderr,
+ " -c p, --pact=p\n"
+ " Probability of acting on a block (default %g).\n"
" -r n, --rinter=n\n"
" Recurse every n passes if n > 0 (default %u).\n"
" -d n, --rmax=n\n"
" Maximum recursion depth (default %u).\n"
" -x n, --seed=n\n"
" Random number seed (default from entropy).\n"
+ " -z, --arena-unzoned\n"
+ " Disabled zoned allocation in the arena\n"
"Tests:\n"
" mvt pool class MVT\n"
" mvff pool class MVFF\n"
" mv pool class MV\n"
" mvb pool class MV with buffers\n"
" an malloc\n",
+ pact,
rinter,
rmax);
return EXIT_FAILURE;
@@ -314,14 +359,16 @@ int main(int argc, char *argv[]) {
argv += optind;
printf("seed: %lu\n", seed);
-
+ (void)fflush(stdout);
+
while (argc > 0) {
- for (i = 0; i < sizeof(pools) / sizeof(pools[0]); ++i)
+ for (i = 0; i < NELEMS(pools); ++i)
if (strcmp(argv[0], pools[i].name) == 0)
goto found;
fprintf(stderr, "unknown pool test \"%s\"\n", argv[0]);
return EXIT_FAILURE;
found:
+ (void)mps_lib_assert_fail_install(assert_die);
rnd_state_set(seed);
pools[i].wrap(pools[i].dj, pools[i].pool_class(), pools[i].name);
--argc;
@@ -334,7 +381,7 @@ int main(int argc, char *argv[]) {
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/event.c b/code/event.c
index 8198319f26..cf9d4e0a8b 100644
--- a/code/event.c
+++ b/code/event.c
@@ -1,7 +1,7 @@
/* event.c: EVENT LOGGING
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .sources: mps.design.event
*
@@ -44,13 +44,13 @@ char EventBuffer[EventKindLIMIT][EventBufferSIZE];
char *EventLast[EventKindLIMIT];
/* Pointers to the last even written out of each buffer. */
-char *EventWritten[EventKindLIMIT];
+static char *EventWritten[EventKindLIMIT];
EventControlSet EventKindControl; /* Bit set used to control output. */
/* A single event structure output once per buffer flush. */
-EventEventClockSyncStruct eventClockSyncStruct;
+static EventEventClockSyncStruct eventClockSyncStruct;
/* eventClockSync -- Populate and write the clock sync event. */
@@ -192,7 +192,8 @@ void EventInit(void)
AVER(size_tAlignUp(sizeof(Event##name##Struct), MPS_PF_ALIGN) \
<= EventSizeMAX); \
AVER(Event##name##Code == code); \
- AVER(0 <= code && code <= EventCodeMAX); \
+ AVER(0 <= code); \
+ AVER(code <= EventCodeMAX); \
AVER(sizeof(#name) - 1 <= EventNameMAX); \
AVER((Bool)Event##name##Always == always); \
AVERT(Bool, always); \
@@ -200,7 +201,7 @@ void EventInit(void)
AVER((EventKind)Event##name##Kind < EventKindLIMIT); \
EVENT_##name##_PARAMS(EVENT_PARAM_CHECK, name)
- EVENT_LIST(EVENT_CHECK, X)
+ EVENT_LIST(EVENT_CHECK, X);
/* Ensure that no event can be larger than the maximum event size. */
AVER(EventBufferSIZE <= EventSizeMAX);
@@ -319,7 +320,7 @@ void EventLabelAddr(Addr addr, EventStringId id)
" $U", (WriteFU)event->name.f##index,
-Res EventDescribe(Event event, mps_lib_FILE *stream)
+Res EventDescribe(Event event, mps_lib_FILE *stream, Count depth)
{
Res res;
@@ -329,15 +330,18 @@ Res EventDescribe(Event event, mps_lib_FILE *stream)
if (stream == NULL)
return ResFAIL;
- res = WriteF(stream,
+ res = WriteF(stream, depth,
"Event $P {\n", (WriteFP)event,
" code $U\n", (WriteFU)event->any.code,
" clock ", NULL);
- if (res != ResOK) return res;
- res = EVENT_CLOCK_WRITE(stream, event->any.clock);
- if (res != ResOK) return res;
- res = WriteF(stream, "\n size $U\n", (WriteFU)event->any.size, NULL);
- if (res != ResOK) return res;
+ if (res != ResOK)
+ return res;
+ res = EVENT_CLOCK_WRITE(stream, depth, event->any.clock);
+ if (res != ResOK)
+ return res;
+ res = WriteF(stream, depth, "\n size $U\n", (WriteFU)event->any.size, NULL);
+ if (res != ResOK)
+ return res;
switch (event->any.code) {
@@ -347,23 +351,25 @@ Res EventDescribe(Event event, mps_lib_FILE *stream)
#define EVENT_DESC(X, name, _code, always, kind) \
case _code: \
- res = WriteF(stream, \
+ res = WriteF(stream, depth, \
" event \"$S\"", (WriteFS)#name, \
EVENT_##name##_PARAMS(EVENT_DESC_PARAM, name) \
NULL); \
- if (res != ResOK) return res; \
+ if (res != ResOK) \
+ return res; \
break;
EVENT_LIST(EVENT_DESC, X)
default:
- res = WriteF(stream, " event type unknown", NULL);
- if (res != ResOK) return res;
+ res = WriteF(stream, depth, " event type unknown", NULL);
+ if (res != ResOK)
+ return res;
/* TODO: Hexdump unknown event contents. */
break;
}
- res = WriteF(stream,
+ res = WriteF(stream, depth,
"\n} Event $P\n", (WriteFP)event,
NULL);
return res;
@@ -374,10 +380,12 @@ Res EventWrite(Event event, mps_lib_FILE *stream)
{
Res res;
- if (event == NULL) return ResFAIL;
- if (stream == NULL) return ResFAIL;
+ if (event == NULL)
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
- res = EVENT_CLOCK_WRITE(stream, event->any.clock);
+ res = EVENT_CLOCK_WRITE(stream, 0, event->any.clock);
if (res != ResOK)
return res;
@@ -388,16 +396,19 @@ Res EventWrite(Event event, mps_lib_FILE *stream)
#define EVENT_WRITE(X, name, code, always, kind) \
case code: \
- res = WriteF(stream, " $S", #name, \
+ res = WriteF(stream, 0, " $S", (WriteFS)#name, \
EVENT_##name##_PARAMS(EVENT_WRITE_PARAM, name) \
NULL); \
- if (res != ResOK) return res; \
+ if (res != ResOK) \
+ return res; \
break;
EVENT_LIST(EVENT_WRITE, X)
default:
- res = WriteF(stream, " ", event->any.code, NULL);
- if (res != ResOK) return res;
+ res = WriteF(stream, 0, " ",
+ (WriteFU)event->any.code, NULL);
+ if (res != ResOK)
+ return res;
/* TODO: Hexdump unknown event contents. */
break;
}
@@ -416,18 +427,18 @@ void EventDump(mps_lib_FILE *stream)
/* This can happen if there's a backtrace very early in the life of
the MPS, and will cause an access violation if we continue. */
if (!eventInited) {
- WriteF(stream, "No events\n", NULL);
+ (void)WriteF(stream, 0, "No events\n", NULL);
return;
}
for (kind = 0; kind < EventKindLIMIT; ++kind) {
for (event = (Event)EventLast[kind];
- event < (Event)(EventBuffer[kind] + EventBufferSIZE);
+ (char *)event < EventBuffer[kind] + EventBufferSIZE;
event = (Event)((char *)event + event->any.size)) {
/* Try to keep going even if there's an error, because this is used as a
backtrace and we'll take what we can get. */
(void)EventWrite(event, stream);
- (void)WriteF(stream, "\n", NULL);
+ (void)WriteF(stream, 0, "\n", NULL);
}
}
}
@@ -468,7 +479,7 @@ EventStringId EventInternString(const char *label)
UNUSED(label);
/* EventInternString is reached in varieties without events, but the result
is not used for anything. */
- return (EventStringId)0x9024EAC8;
+ return (EventStringId)0x4026EAC8;
}
@@ -477,7 +488,7 @@ Word EventInternGenString(size_t len, const char *label)
UNUSED(len); UNUSED(label);
/* EventInternGenString is reached in varieties without events, but
the result is not used for anything. */
- return (EventStringId)0x9024EAC8;
+ return (EventStringId)0x4026EAC8;
}
@@ -490,10 +501,11 @@ void EventLabelAddr(Addr addr, Word id)
}
-Res EventDescribe(Event event, mps_lib_FILE *stream)
+Res EventDescribe(Event event, mps_lib_FILE *stream, Count depth)
{
UNUSED(event);
UNUSED(stream);
+ UNUSED(depth);
return ResUNIMPL;
}
@@ -517,7 +529,7 @@ extern void EventDump(mps_lib_FILE *stream)
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/event.h b/code/event.h
index 3e1463527b..a586509f23 100644
--- a/code/event.h
+++ b/code/event.h
@@ -33,7 +33,7 @@ extern EventStringId EventInternString(const char *label);
extern EventStringId EventInternGenString(size_t, const char *label);
extern void EventLabelAddr(Addr addr, Word id);
extern void EventFlush(EventKind kind);
-extern Res EventDescribe(Event event, mps_lib_FILE *stream);
+extern Res EventDescribe(Event event, mps_lib_FILE *stream, Count depth);
extern Res EventWrite(Event event, mps_lib_FILE *stream);
extern void EventDump(mps_lib_FILE *stream);
@@ -87,7 +87,7 @@ extern Word EventKindControl;
size = offsetof(Event##name##Struct, f1) + _string_len + sizeof('\0'); \
EVENT_BEGIN(name, size) \
_event->f0 = (p0); \
- mps_lib_memcpy(_event->f1, (string), _string_len); \
+ (void)mps_lib_memcpy(_event->f1, (string), _string_len); \
_event->f1[_string_len] = '\0'; \
EVENT_END(name, size); \
END
diff --git a/code/eventcnv.c b/code/eventcnv.c
index 7cca6846a4..baf8b34323 100644
--- a/code/eventcnv.c
+++ b/code/eventcnv.c
@@ -1,5 +1,5 @@
/* eventcnv.c: Simple event log converter
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* This is a command-line tool that converts a binary format telemetry output
* stream from the MPS into a more-portable textual format.
@@ -46,12 +46,6 @@
#include /* for strcmp */
#include "mpstd.h"
-#ifdef MPS_BUILD_MV
-/* MSVC warning 4996 = stdio / C runtime 'unsafe' */
-/* Objects to: strncpy, sscanf, fopen. See job001934. */
-#pragma warning( disable : 4996 )
-#endif
-
#define DEFAULT_TELEMETRY_FILENAME "mpsio.log"
#define TELEMETRY_FILENAME_ENVAR "MPS_TELEMETRY_FILENAME"
@@ -62,18 +56,20 @@ static const char *prog; /* program name */
/* fevwarn -- flush stdout, write message to stderr */
+ATTRIBUTE_FORMAT((printf, 2, 0))
static void fevwarn(const char *prefix, const char *format, va_list args)
{
- fflush(stdout); /* sync */
- fprintf(stderr, "%s: %s @", prog, prefix);
- EVENT_CLOCK_PRINT(stderr, eventTime);
- fprintf(stderr, " ");
- vfprintf(stderr, format, args);
- fprintf(stderr, "\n");
+ (void)fflush(stdout); /* sync */
+ (void)fprintf(stderr, "%s: %s @", prog, prefix);
+ (void)EVENT_CLOCK_PRINT(stderr, eventTime);
+ (void)fprintf(stderr, " ");
+ (void)vfprintf(stderr, format, args);
+ (void)fprintf(stderr, "\n");
}
/* evwarn -- flush stdout, warn to stderr */
+ATTRIBUTE_FORMAT((printf, 1, 2))
static void evwarn(const char *format, ...)
{
va_list args;
@@ -85,6 +81,7 @@ static void evwarn(const char *format, ...)
/* everror -- flush stdout, message to stderr, exit */
+ATTRIBUTE_FORMAT((printf, 1, 2))
static void everror(const char *format, ...)
{
va_list args;
@@ -100,10 +97,9 @@ static void everror(const char *format, ...)
static void usage(void)
{
- fprintf(stderr,
- "Usage: %s [-f logfile] [-h]\n"
- "See \"Telemetry\" in the reference manual for instructions.\n",
- prog);
+ (void)fprintf(stderr, "Usage: %s [-f logfile] [-h]\n"
+ "See \"Telemetry\" in the reference manual for instructions.\n",
+ prog);
}
@@ -176,7 +172,8 @@ static void printParamS(const char *str)
putchar('"');
for (i = 0; str[i] != '\0'; ++i) {
char c = str[i];
- if (c == '"' || c == '\\') putchar('\\');
+ if (c == '"' || c == '\\')
+ putchar('\\');
putchar(c);
}
putchar('"');
@@ -201,6 +198,12 @@ static Res eventRead(Bool *eofOut, EventUnion *event, FILE *stream)
return ResIO;
}
+ if (event->any.size < sizeof(event->any))
+ return ResFAIL; /* invalid size: too small */
+
+ if (event->any.size > sizeof(*event))
+ return ResFAIL; /* invalid size: too large */
+
/* Read the rest of the event. */
rest = event->any.size - sizeof(event->any);
if (rest > 0) {
@@ -268,9 +271,12 @@ static void readLog(FILE *stream)
event->EventInit.f5,
MPS_WORD_WIDTH);
break;
+ default:
+ /* No special treatment needed. */
+ break;
}
- EVENT_CLOCK_PRINT(stdout, eventTime);
+ (void)EVENT_CLOCK_PRINT(stdout, eventTime);
printf(" %4X", (unsigned)code);
switch (code) {
@@ -286,7 +292,7 @@ static void readLog(FILE *stream)
}
putchar('\n');
- fflush(stdout);
+ (void)fflush(stdout);
} /* while(!feof(input)) */
}
@@ -332,7 +338,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/eventcom.h b/code/eventcom.h
index 6114b045d4..931c975596 100644
--- a/code/eventcom.h
+++ b/code/eventcom.h
@@ -1,6 +1,6 @@
/* -- Event Logging Common Definitions
*
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* $Id$
*
* .sources: mps.design.telemetry
@@ -56,7 +56,8 @@ ENUM_DECLARE(EventKind)
enum EventDefinitionsEnum {
EVENT_LIST(EVENT_ENUM, X)
- EventEnumWarningSuppressor /* suppress comma-at-end-of-enum warning */
+ /* suppress comma-at-end-of-enum warning */
+ EventEnumWarningSuppressor = USHRT_MAX
};
@@ -89,7 +90,11 @@ typedef Word EventFW; /* word */
typedef unsigned EventFU; /* unsigned integer */
typedef char EventFS[EventStringLengthMAX + sizeof('\0')]; /* string */
typedef double EventFD; /* double */
-typedef int EventFB; /* boolean */
+/* EventFB must be unsigned (even though Bool is a typedef for int)
+ * because it used as the type of a bitfield with width 1, and we need
+ * the legals values of the field to be 0 and 1 (not 0 and -1 which
+ * would be the case for int : 1). */
+typedef unsigned EventFB; /* Boolean */
/* Event packing bitfield specifiers */
#define EventFP_BITFIELD
@@ -133,7 +138,7 @@ typedef union EventUnion {
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/eventdef.h b/code/eventdef.h
index 0d9e2ea910..081b45de7c 100644
--- a/code/eventdef.h
+++ b/code/eventdef.h
@@ -1,7 +1,7 @@
/* -- Event Logging Definitions
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .source:
*
@@ -31,14 +31,13 @@
* the median version when changing an existing event,
* and the major version when changing the format of the event file.
*
- * TODO: These should go into a header that appears at the start of a
- * telemetry stream, but they aren't currently used. Keep updating them
- * anyway. RB 2012-09-07
+ * These are passed as parameters to the EventInit event at the start
+ * of a telemetry stream, allowing that stream to be identified.
*/
#define EVENT_VERSION_MAJOR ((unsigned)1)
-#define EVENT_VERSION_MEDIAN ((unsigned)1)
-#define EVENT_VERSION_MINOR ((unsigned)6)
+#define EVENT_VERSION_MEDIAN ((unsigned)4)
+#define EVENT_VERSION_MINOR ((unsigned)0)
/* EVENT_LIST -- list of event types and general properties
@@ -96,7 +95,7 @@
EVENT(X, PoolFinish , 0x0016, TRUE, Pool) \
EVENT(X, PoolAlloc , 0x0017, TRUE, Object) \
EVENT(X, PoolFree , 0x0018, TRUE, Object) \
- EVENT(X, CBSInit , 0x0019, TRUE, Pool) \
+ EVENT(X, LandInit , 0x0019, TRUE, Pool) \
EVENT(X, Intern , 0x001a, TRUE, User) \
EVENT(X, Label , 0x001b, TRUE, User) \
EVENT(X, TraceStart , 0x001c, TRUE, Trace) \
@@ -116,8 +115,8 @@
/* TraceScanArea{Tagged} abuses kind, see .kind.abuse */ \
EVENT(X, TraceScanArea , 0x0029, TRUE, Seg) \
EVENT(X, TraceScanAreaTagged, 0x002a, TRUE, Seg) \
- EVENT(X, VMCreate , 0x002b, TRUE, Arena) \
- EVENT(X, VMDestroy , 0x002c, TRUE, Arena) \
+ EVENT(X, VMInit , 0x002b, TRUE, Arena) \
+ EVENT(X, VMFinish , 0x002c, TRUE, Arena) \
EVENT(X, VMMap , 0x002d, TRUE, Seg) \
EVENT(X, VMUnmap , 0x002e, TRUE, Seg) \
EVENT(X, ArenaExtend , 0x002f, TRUE, Arena) \
@@ -188,13 +187,13 @@
EVENT(X, VMCompact , 0x0079, TRUE, Arena) \
EVENT(X, amcScanNailed , 0x0080, TRUE, Seg) \
EVENT(X, AMCTraceEnd , 0x0081, TRUE, Trace) \
- EVENT(X, TraceStartPoolGen , 0x0082, TRUE, Trace) \
+ EVENT(X, TraceCreatePoolGen , 0x0082, TRUE, Trace) \
/* new events for performance analysis of large heaps. */ \
EVENT(X, TraceCondemnZones , 0x0083, TRUE, Trace) \
EVENT(X, ArenaGenZoneAdd , 0x0084, TRUE, Arena) \
EVENT(X, ArenaUseFreeZone , 0x0085, TRUE, Arena) \
- EVENT(X, ArenaBlacklistZone , 0x0086, TRUE, Arena)
-
+ /* EVENT(X, ArenaBlacklistZone , 0x0086, TRUE, Arena) */
+
/* Remember to update EventNameMAX and EventCodeMAX above!
(These are checked in EventInit.) */
@@ -312,8 +311,8 @@
PARAM(X, 1, A, old) \
PARAM(X, 2, W, size)
-#define EVENT_CBSInit_PARAMS(PARAM, X) \
- PARAM(X, 0, P, cbs) \
+#define EVENT_LandInit_PARAMS(PARAM, X) \
+ PARAM(X, 0, P, land) \
PARAM(X, 1, P, owner)
#define EVENT_Intern_PARAMS(PARAM, X) \
@@ -369,12 +368,12 @@
PARAM(X, 1, P, base) \
PARAM(X, 2, P, limit)
-#define EVENT_VMCreate_PARAMS(PARAM, X) \
+#define EVENT_VMInit_PARAMS(PARAM, X) \
PARAM(X, 0, P, vm) \
PARAM(X, 1, A, base) \
PARAM(X, 2, A, limit)
-#define EVENT_VMDestroy_PARAMS(PARAM, X) \
+#define EVENT_VMFinish_PARAMS(PARAM, X) \
PARAM(X, 0, P, vm)
#define EVENT_VMMap_PARAMS(PARAM, X) \
@@ -505,7 +504,8 @@
PARAM(X, 0, P, pool) \
PARAM(X, 1, P, arena) \
PARAM(X, 2, W, extendBy) \
- PARAM(X, 3, W, unitSize)
+ PARAM(X, 3, B, extendSelf) \
+ PARAM(X, 4, W, unitSize)
#define EVENT_PoolInitAMS_PARAMS(PARAM, X) \
PARAM(X, 0, P, pool) \
@@ -692,8 +692,8 @@
#define EVENT_AMCTraceEnd_PARAMS(PARAM, X) \
PARAM(X, 0, W, epoch) /* current arena epoch */ \
PARAM(X, 1, U, why) /* reason trace started */ \
- PARAM(X, 2, W, align) /* arena alignment */ \
- PARAM(X, 3, W, large) /* AMCLargeSegPAGES */ \
+ PARAM(X, 2, W, grainSize) /* arena grain size */ \
+ PARAM(X, 3, W, large) /* AMC large size */ \
PARAM(X, 4, W, pRetMin) /* threshold for event */ \
/* remaining parameters are copy of PageRetStruct, which see */ \
PARAM(X, 5, W, pCond) \
@@ -713,18 +713,18 @@
PARAM(X, 19, W, pRL) \
PARAM(X, 20, W, pRLr)
-#define EVENT_TraceStartPoolGen_PARAMS(PARAM, X) \
- PARAM(X, 0, P, chain) /* chain (or NULL for topGen) */ \
- PARAM(X, 1, B, top) /* 1 for topGen, 0 otherwise */ \
- PARAM(X, 2, W, index) /* index of generation in the chain */ \
- PARAM(X, 3, P, gendesc) /* generation description */ \
- PARAM(X, 4, W, capacity) /* capacity of generation */ \
- PARAM(X, 5, D, mortality) /* mortality of generation */ \
- PARAM(X, 6, W, zone) /* zone set of generation */ \
- PARAM(X, 7, P, pool) /* pool */ \
- PARAM(X, 8, W, serial) /* pool gen serial number */ \
- PARAM(X, 9, W, totalSize) /* total size of pool gen */ \
- PARAM(X, 10, W, newSizeAtCreate) /* new size of pool gen at trace create */
+#define EVENT_TraceCreatePoolGen_PARAMS(PARAM, X) \
+ PARAM(X, 0, P, gendesc) /* generation description */ \
+ PARAM(X, 1, W, capacity) /* capacity of generation */ \
+ PARAM(X, 2, D, mortality) /* mortality of generation */ \
+ PARAM(X, 3, W, zone) /* zone set of generation */ \
+ PARAM(X, 4, P, pool) /* pool */ \
+ PARAM(X, 5, W, totalSize) /* total size of pool gen */ \
+ PARAM(X, 6, W, freeSize) /* free size of pool gen */ \
+ PARAM(X, 7, W, newSize) /* new size of pool gen */ \
+ PARAM(X, 8, W, oldSize) /* old size of pool gen */ \
+ PARAM(X, 9, W, newDeferredSize) /* new size (deferred) of pool gen */ \
+ PARAM(X, 10, W, oldDeferredSize) /* old size (deferred) of pool gen */
#define EVENT_TraceCondemnZones_PARAMS(PARAM, X) \
PARAM(X, 0, P, trace) /* the trace */ \
@@ -733,23 +733,19 @@
#define EVENT_ArenaGenZoneAdd_PARAMS(PARAM, X) \
PARAM(X, 0, P, arena) /* the arena */ \
- PARAM(X, 1, W, gen) /* the generation number */ \
+ PARAM(X, 1, P, gendesc) /* the generation description */ \
PARAM(X, 2, W, zoneSet) /* the new zoneSet */
#define EVENT_ArenaUseFreeZone_PARAMS(PARAM, X) \
PARAM(X, 0, P, arena) /* the arena */ \
PARAM(X, 1, W, zoneSet) /* zones that aren't free any longer */
-#define EVENT_ArenaBlacklistZone_PARAMS(PARAM, X) \
- PARAM(X, 0, P, arena) /* the arena */ \
- PARAM(X, 1, W, zoneSet) /* the blacklist zoneset */
-
#endif /* eventdef_h */
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2013 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/eventrep.c b/code/eventrep.c
index c2f611f707..9dae5056fc 100644
--- a/code/eventrep.c
+++ b/code/eventrep.c
@@ -1,5 +1,5 @@
/* eventrep.c: Allocation replayer routines
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* $Id$
*/
@@ -31,7 +31,7 @@
#include "mpstd.h"
-#ifdef MPS_PF_W3I6MV
+#if defined(MPS_OS_W3) && defined(MPS_ARCH_I6)
#define PRIuLONGEST "llu"
#define PRIXPTR "016llX"
typedef unsigned long long ulongest_t;
@@ -116,6 +116,7 @@ typedef struct apRepStruct *apRep;
/* error -- error signalling */
+ATTRIBUTE_FORMAT((printf, 1, 2))
static void error(const char *format, ...)
{
va_list args;
@@ -142,37 +143,6 @@ static void error(const char *format, ...)
MPS_BEGIN if (!(cond)) error("line %d " #cond, __LINE__); MPS_END
-#ifdef MPS_PROD_EPCORE
-
-
-/* ensurePSFormat -- return the PS format, creating it, if necessary */
-
-static mps_fmt_t psFormat = NULL;
-
-static void ensurePSFormat(mps_fmt_t *fmtOut, mps_arena_t arena)
-{
- mps_res_t eres;
-
- if (psFormat == NULL) {
- eres = mps_fmt_create_A(&psFormat, arena, ps_fmt_A());
- verifyMPS(eres);
- }
- *fmtOut = psFormat;
-}
-
-
-/* finishPSFormat -- finish the PS format, if necessary */
-
-static void finishPSFormat(void)
-{
- if (psFormat != NULL)
- mps_fmt_destroy(psFormat);
-}
-
-
-#endif
-
-
/* objectTableCreate -- create an objectTable */
static objectTable objectTableCreate(poolSupport support)
@@ -272,7 +242,8 @@ static void objRemove(void **objReturn, objectTable table,
/* poolRecreate -- create and record a pool */
-static void poolRecreate(void *logPool, void *logArena, mps_class_t class,
+static void poolRecreate(void *logPool, void *logArena,
+ mps_pool_class_t pool_class,
poolSupport support, int bufferClassLevel, ...)
{
va_list args;
@@ -417,10 +388,6 @@ void EventReplay(Event event, Word etime)
case EventArenaDestroy: { /* arena */
found = TableLookup(&entry, arenaTable, (Word)event->p.p0);
verify(found);
-#ifdef MPS_PROD_EPCORE
- /* @@@@ assuming there's only one arena at a time */
- finishPSFormat();
-#endif
mps_arena_destroy((mps_arena_t)entry);
ires = TableRemove(arenaTable, (Word)event->pw.p0);
verify(ires == ResOK);
@@ -455,30 +422,6 @@ void EventReplay(Event event, Word etime)
/* all internal only */
++discardedEvents;
} break;
-#ifdef MPS_PROD_EPCORE
- case EventPoolInitEPVM: {
- /* pool, arena, format, maxSaveLevel, saveLevel */
- mps_arena_t arena;
- mps_fmt_t format;
-
- found = TableLookup(&entry, arenaTable, (Word)event->pppuu.p1);
- verify(found);
- arena = (mps_arena_t)entry;
- ensurePSFormat(&format, arena); /* We know what the format is. */
- poolRecreate(event->pppuu.p0, event->pppuu.p1,
- mps_class_epvm(), supportNothing, 2, format,
- (mps_epvm_save_level_t)event->pppuu.u3,
- (mps_epvm_save_level_t)event->pppuu.u4);
- } break;
- case EventPoolInitEPDL: {
- /* pool, arena, isEPDL, extendBy, avgSize, align */
- poolRecreate(event->ppuwww.p0, event->ppuwww.p1,
- event->ppuwww.u2 ? mps_class_epdl() : mps_class_epdr(),
- event->ppuwww.u2 ? supportTruncate : supportFree, 0,
- (size_t)event->ppuwww.w3, (size_t)event->ppuwww.w4,
- (size_t)event->ppuwww.w5);
- } break;
-#endif
case EventPoolFinish: { /* pool */
found = TableLookup(&entry, poolTable, (Word)event->p.p0);
if (found) {
@@ -541,22 +484,6 @@ void EventReplay(Event event, Word etime)
++discardedEvents;
}
} break;
-#ifdef MPS_PROD_EPCORE
- case EventBufferInitEPVM: { /* buffer, pool, isObj */
- found = TableLookup(&entry, poolTable, (Word)event->ppu.p1);
- if (found) {
- poolRep rep = (poolRep)entry;
-
- if(rep->bufferClassLevel == 2) { /* see .bufclass */
- apRecreate(event->ppu.p0, event->ppu.p1, (mps_bool_t)event->ppu.u2);
- } else {
- ++discardedEvents;
- }
- } else {
- ++discardedEvents;
- }
- } break;
-#endif
case EventBufferFinish: { /* buffer */
found = TableLookup(&entry, apTable, (Word)event->p.p0);
if (found) {
@@ -619,26 +546,6 @@ void EventReplay(Event event, Word etime)
++discardedEvents;
}
} break;
-#ifdef MPS_PROD_EPCORE
- case EventPoolPush: { /* pool */
- found = TableLookup(&entry, poolTable, (Word)event->p.p0);
- if (found) {
- poolRep rep = (poolRep)entry;
-
- /* It must be EPVM. */
- mps_epvm_save(rep->pool);
- }
- } break;
- case EventPoolPop: { /* pool, level */
- found = TableLookup(&entry, poolTable, (Word)event->pu.p0);
- if (found) {
- poolRep rep = (poolRep)entry;
-
- /* It must be EPVM. */
- mps_epvm_restore(rep->pool, (mps_epvm_save_level_t)event->pu.u1);
- }
- } break;
-#endif
case EventCommitLimitSet: { /* arena, limit, succeeded */
found = TableLookup(&entry, arenaTable, (Word)event->pwu.p0);
verify(found);
@@ -659,7 +566,7 @@ void EventReplay(Event event, Word etime)
mps_reservoir_limit_set((mps_arena_t)entry, (size_t)event->pw.w1);
} break;
case EventVMMap: case EventVMUnmap:
- case EventVMCreate: case EventVMDestroy:
+ case EventVMInit: case EventVMFinish:
case EventArenaWriteFaults:
case EventArenaAlloc: case EventArenaAllocFail: case EventArenaFree:
case EventSegAlloc: case EventSegAllocFail: case EventSegFree:
@@ -713,11 +620,14 @@ Res EventRepInit(void)
totalEvents = 0; discardedEvents = 0; unknownEvents = 0;
res = TableCreate(&arenaTable, (size_t)1);
- if (res != ResOK) goto failArena;
+ if (res != ResOK)
+ goto failArena;
res = TableCreate(&poolTable, (size_t)1<<4);
- if (res != ResOK) goto failPool;
+ if (res != ResOK)
+ goto failPool;
res = TableCreate(&apTable, (size_t)1<<6);
- if (res != ResOK) goto failAp;
+ if (res != ResOK)
+ goto failAp;
return ResOK;
@@ -744,7 +654,7 @@ void EventRepFinish(void)
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/eventsql.c b/code/eventsql.c
index 0fa36dea73..0883862077 100644
--- a/code/eventsql.c
+++ b/code/eventsql.c
@@ -2,7 +2,7 @@
*
* $Id$
*
- * Copyright (c) 2012-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2012-2014 Ravenbrook Limited. See end of file for license.
*
* This is a command-line tool that imports events from a text-format
* MPS telemetry file into a SQLite database file.
@@ -86,9 +86,6 @@
#define DEFAULT_DATABASE_NAME "mpsevent.db"
#ifdef MPS_BUILD_MV
-/* MSVC warning 4996 = stdio / C runtime 'unsafe' */
-/* Objects to: getenv, sprintf. See job001934. */
-#pragma warning( disable : 4996 )
#define strtoll _strtoi64
#endif
@@ -105,7 +102,7 @@ typedef sqlite3_int64 int64;
* and for reporting errors.
*/
-unsigned int verbosity = 0;
+static unsigned int verbosity = 0;
#define LOG_ALWAYS 0
#define LOG_OFTEN 1
@@ -113,6 +110,7 @@ unsigned int verbosity = 0;
#define LOG_SELDOM 3
#define LOG_RARELY 4
+ATTRIBUTE_FORMAT((printf, 2, 0))
static void vlog(unsigned int level, const char *format, va_list args)
{
if (level <= verbosity) {
@@ -123,6 +121,7 @@ static void vlog(unsigned int level, const char *format, va_list args)
}
}
+ATTRIBUTE_FORMAT((printf, 2, 3))
static void evlog(unsigned int level, const char *format, ...)
{
va_list args;
@@ -131,6 +130,7 @@ static void evlog(unsigned int level, const char *format, ...)
va_end(args);
}
+ATTRIBUTE_FORMAT((printf, 1, 2))
static void error(const char *format, ...)
{
va_list args;
@@ -386,7 +386,7 @@ static void testTableExists(sqlite3 *db)
size_t i;
int defects = 0;
int tests = 0;
- for (i=0; i < (sizeof(tableTests)/sizeof(tableTests[0])); ++i) {
+ for (i=0; i < NELEMS(tableTests); ++i) {
const char *name = tableTests[i].name;
int exists = tableExists(db, name);
if (exists)
@@ -452,7 +452,7 @@ static void registerLogFile(sqlite3 *db,
name = sqlite3_column_text(statement, 0);
logSerial = sqlite3_column_int64(statement, 1);
completed = sqlite3_column_int64(statement, 2);
- evlog(force ? LOG_OFTEN : LOG_ALWAYS, "Log file matching '%s' already in event_log, named \"%s\" (serial %lu, completed %lu).",
+ evlog(force ? LOG_OFTEN : LOG_ALWAYS, "Log file matching '%s' already in event_log, named \"%s\" (serial %llu, completed %llu).",
filename, name, logSerial, completed);
if (force) {
evlog(LOG_OFTEN, "Continuing anyway because -f specified.");
@@ -486,7 +486,7 @@ static void registerLogFile(sqlite3 *db,
if (res != SQLITE_DONE)
sqlite_error(res, db, "insert into event_log failed.");
logSerial = sqlite3_last_insert_rowid(db);
- evlog(LOG_SOMETIMES, "Log file %s added to event_log with serial %lu",
+ evlog(LOG_SOMETIMES, "Log file %s added to event_log with serial %llu",
filename, logSerial);
finalizeStatement(db, statement);
}
@@ -508,7 +508,7 @@ static void logFileCompleted(sqlite3 *db,
res = sqlite3_step(statement);
if (res != SQLITE_DONE)
sqlite_error(res, db, "insert into event_log failed.");
- evlog(LOG_SOMETIMES, "Marked in event_log: %lu events", completed);
+ evlog(LOG_SOMETIMES, "Marked in event_log: %llu events", completed);
finalizeStatement(db, statement);
}
@@ -533,7 +533,7 @@ static void logFileCompleted(sqlite3 *db,
/* An array of table-creation statement strings. */
-const char *createStatements[] = {
+static const char *createStatements[] = {
"CREATE TABLE IF NOT EXISTS event_kind (name TEXT,"
" description TEXT,"
" enum INTEGER PRIMARY KEY)",
@@ -566,12 +566,12 @@ static void makeTables(sqlite3 *db)
size_t i;
evlog(LOG_SOMETIMES, "Creating tables.");
- for (i=0; i < (sizeof(createStatements)/sizeof(createStatements[0])); ++i) {
+ for (i=0; i < NELEMS(createStatements); ++i) {
runStatement(db, createStatements[i], "Table creation");
}
}
-const char *glueTables[] = {
+static const char *glueTables[] = {
"event_kind",
"event_type",
"event_param",
@@ -585,7 +585,7 @@ static void dropGlueTables(sqlite3 *db)
evlog(LOG_ALWAYS, "Dropping glue tables so they are rebuilt.");
- for (i=0; i < (sizeof(glueTables)/sizeof(glueTables[0])); ++i) {
+ for (i=0; i < NELEMS(glueTables); ++i) {
evlog(LOG_SOMETIMES, "Dropping table %s", glueTables[i]);
sprintf(sql, "DROP TABLE %s", glueTables[i]);
res = sqlite3_exec(db,
@@ -864,7 +864,7 @@ static int64 readLog(FILE *input,
/* this macro sets statement and last_index */
EVENT_LIST(EVENT_TYPE_WRITE_SQL, X);
default:
- error("Event %llu has Unknown event code %d", eventCount, code);
+ error("Event %llu has Unknown event code %ld", eventCount, code);
}
/* bind the fields we store for every event */ \
res = sqlite3_bind_int64(statement, last_index+1, logSerial);
@@ -951,7 +951,7 @@ int main(int argc, char *argv[])
makeTables(db);
fillGlueTables(db);
count = writeEventsToSQL(db);
- evlog(LOG_ALWAYS, "Imported %llu events from %s to %s, serial %lu.",
+ evlog(LOG_ALWAYS, "Imported %llu events from %s to %s, serial %llu.",
count, logFileName, databaseName, logSerial);
if (runTests) {
@@ -965,7 +965,7 @@ int main(int argc, char *argv[])
/* COPYRIGHT AND LICENSE
*
- * Copyright (c) 2012-2013 Ravenbrook Limited .
+ * Copyright (c) 2012-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/eventtxt.c b/code/eventtxt.c
index 3971d0e84d..7091474076 100644
--- a/code/eventtxt.c
+++ b/code/eventtxt.c
@@ -2,7 +2,7 @@
*
* $Id$
*
- * Copyright (c) 2012-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2012-2014 Ravenbrook Limited. See end of file for license.
*
* This is a command-line tool that converts events from a text-format
* MPS telemetry file into a more human-readable format.
@@ -29,51 +29,50 @@
* $Id$
*/
+#include "check.h"
#include "config.h"
-#include "eventdef.h"
#include "eventcom.h"
+#include "eventdef.h"
+#include "mps.h"
+#include "mpsavm.h"
+#include "mpscmvff.h"
#include "table.h"
#include "testlib.h" /* for ulongest_t and associated print formats */
+#include
+#include
#include
-#include
-#include
-
-#ifdef MPS_BUILD_MV
-/* MSVC warning 4996 = stdio / C runtime 'unsafe' */
-/* Objects to: strncpy, sscanf, fopen. See job001934. */
-#pragma warning( disable : 4996 )
-#endif
+#include /* exit, EXIT_FAILURE, EXIT_SUCCESS */
+#include /* strcpy, strerror, strlen */
static const char *prog; /* program name */
static const char *logFileName = NULL;
/* everror -- error signalling */
+ATTRIBUTE_FORMAT((printf, 1, 2))
static void everror(const char *format, ...)
{
va_list args;
- fflush(stdout); /* sync */
- fprintf(stderr, "%s: ", prog);
+ (void)fflush(stdout); /* sync */
+ (void)fprintf(stderr, "%s: ", prog);
va_start(args, format);
- vfprintf(stderr, format, args);
- fprintf(stderr, "\n");
+ (void)vfprintf(stderr, format, args);
+ (void)fprintf(stderr, "\n");
va_end(args);
exit(EXIT_FAILURE);
}
static void usage(void)
{
- fprintf(stderr,
- "Usage: %s [-l ]\n",
- prog);
+ (void)fprintf(stderr, "Usage: %s [-l ]\n", prog);
}
static void usageError(void)
{
- usage();
- everror("Bad usage");
+ usage();
+ everror("Bad usage");
}
/* parseArgs -- parse command line arguments */
@@ -113,15 +112,19 @@ static void parseArgs(int argc, char *argv[])
static void *tableAlloc(void *closure, size_t size)
{
- UNUSED(closure);
- return malloc(size);
+ mps_pool_t pool = closure;
+ mps_addr_t p;
+ mps_res_t res;
+ res = mps_alloc(&p, pool, size);
+ if (res != MPS_RES_OK)
+ everror("allocation failed: %d", res);
+ return p;
}
static void tableFree(void *closure, void *p, size_t size)
{
- UNUSED(closure);
- UNUSED(size);
- free(p);
+ mps_pool_t pool = closure;
+ mps_free(pool, p, size);
}
/* Printing routines */
@@ -135,16 +138,33 @@ static void printStr(const char *str)
putchar('"');
for (i = 0; str[i] != '\0'; ++i) {
char c = str[i];
- if (c == '"' || c == '\\') putchar('\\');
+ if (c == '"' || c == '\\')
+ putchar('\\');
putchar(c);
}
putchar('"');
}
-/* Reading hex numbers, and doubles, and quoted-and-escaped
+/* Reading clocks, hex numbers, and doubles, and quoted-and-escaped
* strings. */
+static EventClock parseClock(char **pInOut)
+{
+ EventClock val;
+ int i, l;
+ unsigned long low, high;
+ char *p = *pInOut;
+
+ i = sscanf(p, "%08lX%08lX%n", &high, &low, &l);
+ if (i != 2)
+ everror("Couldn't read a clock from '%s'", p);
+ EVENT_CLOCK_MAKE(val, low, high);
+
+ *pInOut = p + l;
+ return val;
+}
+
static ulongest_t parseHex(char **pInOut)
{
ulongest_t val;
@@ -178,7 +198,7 @@ static double parseDouble(char **pInOut)
#define MAX_STRING_LENGTH 1024
-char strBuf[MAX_STRING_LENGTH];
+static char strBuf[MAX_STRING_LENGTH];
static char *parseString(char **pInOut)
{
@@ -222,21 +242,21 @@ static Table internTable; /* dictionary of intern ids to strings */
static Table labelTable; /* dictionary of addrs to intern ids */
-static void createTables(void)
+static void createTables(mps_pool_t pool)
{
Res res;
/* MPS intern IDs are serials from zero up, so we can use -1
* and -2 as specials. */
res = TableCreate(&internTable,
(size_t)1<<4,
- tableAlloc, tableFree, NULL,
+ tableAlloc, tableFree, pool,
(Word)-1, (Word)-2);
if (res != ResOK)
everror("Couldn't make intern table.");
/* We assume that 0 and 1 are invalid as Addrs. */
res = TableCreate(&labelTable, (size_t)1<<7,
- tableAlloc, tableFree, NULL,
+ tableAlloc, tableFree, pool,
0, 1);
if (res != ResOK)
everror("Couldn't make label table.");
@@ -245,19 +265,19 @@ static void createTables(void)
/* recordIntern -- record an interned string in the table. a copy of
* the string from the parsed buffer into a newly-allocated block. */
-static void recordIntern(char *p)
+static void recordIntern(mps_pool_t pool, char *p)
{
ulongest_t stringId;
char *string;
- char *copy;
+ mps_addr_t copy;
size_t len;
Res res;
stringId = parseHex(&p);
string = parseString(&p);
len = strlen(string);
- copy = malloc(len+1);
- if (copy == NULL)
+ res = mps_alloc(©, pool, len + 1);
+ if (res != MPS_RES_OK)
everror("Couldn't allocate space for a string.");
(void)strcpy(copy, string);
res = TableDefine(internTable, (Word)stringId, (void *)copy);
@@ -265,12 +285,55 @@ static void recordIntern(char *p)
everror("Couldn't create an intern mapping.");
}
-/* recordLabel records a label (an association between an address and
- * a string ID). Note that the event log may have been generated on a
- * platform with addresses larger than Word on the current platform.
- * If that happens then we are scuppered because our Table code uses
- * Word as the key type: there's nothing we can do except detect this
- * bad case (see also the EventInit handling and warning code).
+/* Over time there may be multiple labels associated with an address,
+ * so we keep a list, recording for each label the clock when the
+ * association was made. This means that printAddr can select the
+ * label that was in force at the time of the event.
+ */
+
+typedef struct LabelStruct *Label;
+typedef struct LabelStruct {
+ EventClock clock; /* clock of this label */
+ ulongest_t id; /* string id of this label */
+} LabelStruct;
+
+typedef struct LabelListStruct *LabelList;
+typedef struct LabelListStruct {
+ size_t n; /* number of labels in array */
+ Label labels; /* labels, sorted in order by clock */
+} LabelListStruct;
+
+/* labelFind returns the index of the first entry in list with a clock
+ * value that's greater than 'clock', or list->n if there is no such
+ * label. The list is assumed to be sorted.
+ */
+
+static size_t labelFind(LabelList list, EventClock clock)
+{
+ size_t low = 0, high = list->n;
+ while (low < high) {
+ size_t mid = (low + high) / 2;
+ assert(NONNEGATIVE(mid) && mid < list->n);
+ if (list->labels[mid].clock > clock) {
+ high = mid;
+ } else {
+ low = mid + 1;
+ }
+ }
+ assert(NONNEGATIVE(low) && low <= list->n);
+ assert(low == list->n || list->labels[low].clock > clock);
+ return low;
+}
+
+/* recordLabel records a label: an association (made at the time given
+ * by 'clock') between an address and a string ID. These are encoded
+ * as two hexadecimal numbers in the string pointed to by 'p'.
+ *
+ * Note that the event log may have been generated on a platform with
+ * addresses larger than Word on the current platform. If that happens
+ * then we are scuppered because our Table code uses Word as the key
+ * type: there's nothing we can do except detect this bad case (see
+ * also the EventInit handling and warning code).
*
* We can and do handle the case where string IDs (which are Words on
* the MPS platform) are larger than void* on the current platform.
@@ -281,25 +344,50 @@ static void recordIntern(char *p)
* probably a bad idea and maybe doomed to failure.
*/
-static void recordLabel(char *p)
+static void recordLabel(mps_pool_t pool, EventClock clock, char *p)
{
ulongest_t address;
- ulongest_t *stringIdP;
+ LabelList list;
+ Label newlabels;
+ mps_addr_t tmp;
+ size_t pos;
Res res;
-
+
address = parseHex(&p);
if (address > (Word)-1) {
- printf("label address too large!");
+ (void)printf("label address too large!");
return;
}
-
- stringIdP = malloc(sizeof(ulongest_t));
- if (stringIdP == NULL)
- everror("Can't allocate space for a string's ID");
- *stringIdP = parseHex(&p);
- res = TableDefine(labelTable, (Word)address, (void *)stringIdP);
+
+ if (TableLookup(&tmp, labelTable, address)) {
+ list = tmp;
+ } else {
+ /* First label for this address */
+ res = mps_alloc(&tmp, pool, sizeof(LabelListStruct));
+ if (res != MPS_RES_OK)
+ everror("Can't allocate space for a label list");
+ list = tmp;
+ list->n = 0;
+ res = TableDefine(labelTable, (Word)address, list);
+ if (res != ResOK)
+ everror("Couldn't create a label mapping.");
+ }
+
+ res = mps_alloc(&tmp, pool, sizeof(LabelStruct) * (list->n + 1));
if (res != ResOK)
- everror("Couldn't create an intern mapping.");
+ everror("Couldn't allocate space for list of labels.");
+ newlabels = tmp;
+
+ pos = labelFind(list, clock);
+ memcpy(newlabels, list->labels, sizeof(LabelStruct) * pos);
+ newlabels[pos].clock = clock;
+ newlabels[pos].id = parseHex(&p);
+ memcpy(newlabels + pos + 1, list->labels + pos,
+ sizeof(LabelStruct) * (list->n - pos));
+ if (list->n > 0)
+ mps_free(pool, list->labels, sizeof(LabelStruct) * list->n);
+ list->labels = newlabels;
+ ++ list->n;
}
/* output code */
@@ -315,20 +403,23 @@ static int hexWordWidth = (MPS_WORD_WIDTH+3)/4;
/* printAddr -- output a ulongest_t in hex, with the interned string
* if the value is in the label table */
-static void printAddr(ulongest_t addr, const char *ident)
+static void printAddr(EventClock clock, ulongest_t addr, const char *ident)
{
- ulongest_t label;
- void *alias;
+ void *tmp;
printf("%s:%0*" PRIXLONGEST, ident, hexWordWidth, addr);
- if (TableLookup(&alias, labelTable, addr)) {
- label = *(ulongest_t*)alias;
- putchar('[');
- if (TableLookup(&alias, internTable, label))
- printStr((char *)alias);
- else
- printf("unknown label %" PRIuLONGEST, label);
- putchar(']');
+ if (TableLookup(&tmp, labelTable, addr)) {
+ LabelList list = tmp;
+ size_t pos = labelFind(list, clock);
+ if (pos > 0) {
+ ulongest_t id = list->labels[pos - 1].id;
+ putchar('[');
+ if (TableLookup(&tmp, internTable, id))
+ printStr((char *)tmp);
+ else
+ printf("unknown label %" PRIXLONGEST, id);
+ putchar(']');
+ }
}
putchar(' ');
}
@@ -339,7 +430,7 @@ static void printAddr(ulongest_t addr, const char *ident)
#define processParamA(ident) \
val_hex = parseHex(&p); \
- printAddr(val_hex, #ident);
+ printAddr(clock, val_hex, #ident);
#define processParamP processParamA
#define processParamW processParamA
@@ -382,7 +473,7 @@ static const char *eventName[EventCodeMAX+EventCodeMAX];
/* readLog -- read and parse log. Returns the number of events written. */
-static void readLog(FILE *input)
+static void readLog(mps_pool_t pool, FILE *input)
{
int i;
@@ -394,7 +485,7 @@ static void readLog(FILE *input)
while (TRUE) { /* loop for each event */
char line[MAX_LOG_LINE_LENGTH];
char *p, *q;
- ulongest_t clock;
+ EventClock clock;
int code;
ulongest_t val_hex;
double val_float;
@@ -408,23 +499,23 @@ static void readLog(FILE *input)
everror("Couldn't read line from input.");
}
- clock = parseHex(&p);
- code = (int)parseHex(&p);
+ clock = parseClock(&p);
+ EVENT_CLOCK_PRINT(stdout, clock);
+ code = (int)parseHex(&p);
+ printf(" %04X ", code);
if (eventName[code])
- printf("%0*" PRIXLONGEST " %04X %-19s ", hexWordWidth, clock, code,
- eventName[code]);
+ printf("%-19s ", eventName[code]);
else
- printf("%0*" PRIXLONGEST " %04X %-19s ", hexWordWidth, clock, code,
- "[Unknown]");
+ printf("%-19s ", "[Unknown]");
q = p;
/* for a few particular codes, we do local processing. */
if (code == EventInternCode) {
- recordIntern(q);
+ recordIntern(pool, q);
} else if (code == EventLabelCode) {
- recordLabel(q);
+ recordLabel(pool, clock, q);
} else if (code == EventEventInitCode) {
ulongest_t major, median, minor, maxCode, maxNameLen, wordWidth, clocksPerSec;
major = parseHex(&q); /* EVENT_VERSION_MAJOR */
@@ -440,24 +531,27 @@ static void readLog(FILE *input)
if ((major != EVENT_VERSION_MAJOR) ||
(median != EVENT_VERSION_MEDIAN) ||
(minor != EVENT_VERSION_MINOR)) {
- fprintf(stderr, "Event log version does not match: %d.%d.%d vs %d.%d.%d\n",
- (int)major, (int)median, (int)minor,
- EVENT_VERSION_MAJOR,
- EVENT_VERSION_MEDIAN,
- EVENT_VERSION_MINOR);
+ (void)fprintf(stderr, "Event log version does not match: "
+ "%d.%d.%d vs %d.%d.%d\n",
+ (int)major, (int)median, (int)minor,
+ EVENT_VERSION_MAJOR,
+ EVENT_VERSION_MEDIAN,
+ EVENT_VERSION_MINOR);
}
if (maxCode > EventCodeMAX) {
- fprintf(stderr, "Event log may contain unknown events with codes from %d to %d\n",
- EventCodeMAX+1, (int)maxCode);
+ (void)fprintf(stderr, "Event log may contain unknown events "
+ "with codes from %d to %d\n",
+ EventCodeMAX+1, (int)maxCode);
}
if (wordWidth > MPS_WORD_WIDTH) {
int newHexWordWidth = (int)((wordWidth + 3) / 4);
if (newHexWordWidth > hexWordWidth) {
- fprintf(stderr,
- "Event log word width is greater than on current platform;"
- "previous values may be printed too narrowly.\n");
+ (void)fprintf(stderr,
+ "Event log word width is greater than on current "
+ "platform; previous values may be printed too "
+ "narrowly.\n");
}
hexWordWidth = newHexWordWidth;
}
@@ -480,6 +574,9 @@ static void readLog(FILE *input)
int main(int argc, char *argv[])
{
+ mps_arena_t arena;
+ mps_pool_t pool;
+ mps_res_t res;
FILE *input;
parseArgs(argc, argv);
@@ -492,15 +589,32 @@ int main(int argc, char *argv[])
everror("unable to open %s", logFileName);
}
- createTables();
- readLog(input);
+ /* Ensure no telemetry output. */
+ res = setenv("MPS_TELEMETRY_CONTROL", "0", 1);
+ if (res != 0)
+ everror("failed to set MPS_TELEMETRY_CONTROL: %s", strerror(errno));
+
+ res = mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none);
+ if (res != MPS_RES_OK)
+ everror("failed to create arena: %d", res);
+
+ res = mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none);
+ if (res != MPS_RES_OK)
+ everror("failed to create pool: %d", res);
+
+ createTables(pool);
+ readLog(pool, input);
+
+ mps_pool_destroy(pool);
+ mps_arena_destroy(arena);
+
(void)fclose(input);
return 0;
}
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2012-2013 Ravenbrook Limited .
+ * Copyright (C) 2012-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/exposet0.c b/code/exposet0.c
index b204b59d26..7e097d6b03 100644
--- a/code/exposet0.c
+++ b/code/exposet0.c
@@ -1,7 +1,7 @@
/* exposet0.c: ARENA EXPOSE TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (C) 2002 Global Graphics Software.
*
* The primary purpose of this test is to test that mps_arena_expose does
@@ -19,12 +19,9 @@
#include "mpscamc.h"
#include "mpsavm.h"
#include "mpstd.h"
-#ifdef MPS_OS_W3
-#include "mpsw3.h"
-#endif
#include "mps.h"
-#include
-#include
+
+#include /* fflush, printf, puts, stdout */
/* These values have been tuned in the hope of getting one dynamic collection. */
@@ -75,12 +72,6 @@ static void report(mps_arena_t arena)
printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned);
mps_message_discard(arena, message);
-
- if (condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024)
- /* When condemned size is larger than could happen in a gen 2
- * collection (discounting ramps, natch), guess that was a dynamic
- * collection, and reset the commit limit, so it doesn't run out. */
- die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit");
}
}
@@ -115,15 +106,7 @@ static void test_stepper(mps_addr_t object, mps_fmt_t fmt, mps_pool_t pool,
testlib_unused(fmt);
testlib_unused(pool);
testlib_unused(s);
-#ifdef MPS_OS_W3
- __try {
- dylan_mutate(object);
- } __except(EXCEPTION_EXECUTE_HANDLER) {
- error("Unexpected exception.\n");
- }
-#else
dylan_mutate(object);
-#endif
(*(unsigned long *)p)++;
}
@@ -185,7 +168,8 @@ static void *test(void *arg, size_t s)
if (collections != c) {
collections = c;
- printf("\nCollection %lu started, %lu objects.\n", c, objs);
+ printf("\nCollection %"PRIuLONGEST" started, %lu objects.\n",
+ (ulongest_t)c, objs);
report(arena);
for (i = 0; i < exactRootsCOUNT; ++i) {
@@ -231,13 +215,14 @@ static void *test(void *arg, size_t s)
if (objs % 1024 == 0) {
report(arena);
putchar('.');
- fflush(stdout);
+ (void)fflush(stdout);
}
++objs;
}
(void)mps_commit(busy_ap, busy_init, 64);
+ mps_arena_park(arena);
mps_ap_destroy(busy_ap);
mps_ap_destroy(ap);
mps_root_destroy(exactRoot);
@@ -255,13 +240,12 @@ int main(int argc, char *argv[])
mps_thr_t thread;
void *r;
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
+ testlib_init(argc, argv);
die(mps_arena_create(&arena, mps_arena_class_vm(), 2*testArenaSIZE),
"arena_create");
mps_message_type_enable(arena, mps_message_type_gc());
- die(mps_arena_commit_limit_set(arena, testArenaSIZE), "set limit");
+ die(mps_arena_commit_limit_set(arena, 2*testArenaSIZE), "set limit");
die(mps_thread_reg(&thread, arena), "thread_reg");
mps_tramp(&r, test, arena, 0);
mps_thread_dereg(thread);
@@ -275,7 +259,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/expt825.c b/code/expt825.c
index 9c7c4556d7..bd6c5f2b1e 100644
--- a/code/expt825.c
+++ b/code/expt825.c
@@ -1,7 +1,7 @@
/* expt825.c: Test for bug described in job000825
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (C) 2002 Global Graphics Software.
*
* DESIGN
@@ -34,10 +34,8 @@
#include "fmtdy.h"
#include "fmtdytst.h"
#include "mpstd.h"
-#ifdef MPS_OS_W3
-#include "mpsw3.h"
-#endif
-#include
+
+#include /* printf, fflush, stdout */
#define testArenaSIZE ((size_t)16<<20)
@@ -86,7 +84,8 @@ static void register_numbered_tree(mps_word_t tree, mps_arena_t arena)
{
/* don't finalize ints */
if ((tree & 1) == 0) {
- mps_finalize(arena, (mps_addr_t *)&tree);
+ mps_addr_t addr = (void *)tree;
+ die(mps_finalize(arena, &addr), "mps_finalize");
register_numbered_tree(DYLAN_VECTOR_SLOT(tree, 0), arena);
register_numbered_tree(DYLAN_VECTOR_SLOT(tree, 1), arena);
}
@@ -124,8 +123,8 @@ static void register_indirect_tree(mps_word_t tree, mps_arena_t arena)
{
/* don't finalize ints */
if ((tree & 1) == 0) {
- mps_word_t indirect = DYLAN_VECTOR_SLOT(tree,2);
- mps_finalize(arena, (mps_addr_t *)&indirect);
+ mps_addr_t indirect = (void *)DYLAN_VECTOR_SLOT(tree,2);
+ die(mps_finalize(arena, &indirect), "mps_finalize");
register_indirect_tree(DYLAN_VECTOR_SLOT(tree, 0), arena);
register_indirect_tree(DYLAN_VECTOR_SLOT(tree, 1), arena);
}
@@ -186,7 +185,7 @@ static void *test(void *arg, size_t s)
(mps_collections(arena) < collectionCOUNT)) {
mps_word_t final_this_time = 0;
printf("Collecting...");
- fflush(stdout);
+ (void)fflush(stdout);
die(mps_arena_collect(arena), "collect");
printf(" Done.\n");
while (mps_message_poll(arena)) {
@@ -202,8 +201,10 @@ static void *test(void *arg, size_t s)
testlib_unused(obj);
}
finals += final_this_time;
- printf("%lu objects finalized: total %lu of %lu\n",
- final_this_time, finals, object_count);
+ printf("%"PRIuLONGEST" objects finalized: total %"PRIuLONGEST
+ " of %"PRIuLONGEST"\n",
+ (ulongest_t)final_this_time, (ulongest_t)finals,
+ (ulongest_t)object_count);
}
object_count = 0;
@@ -227,7 +228,7 @@ static void *test(void *arg, size_t s)
(mps_collections(arena) < collectionCOUNT)) {
mps_word_t final_this_time = 0;
printf("Collecting...");
- fflush(stdout);
+ (void)fflush(stdout);
die(mps_arena_collect(arena), "collect");
printf(" Done.\n");
while (mps_message_poll(arena)) {
@@ -243,10 +244,13 @@ static void *test(void *arg, size_t s)
testlib_unused(obj);
}
finals += final_this_time;
- printf("%lu objects finalized: total %lu of %lu\n",
- final_this_time, finals, object_count);
+ printf("%"PRIuLONGEST" objects finalized: total %"PRIuLONGEST
+ " of %"PRIuLONGEST"\n",
+ (ulongest_t)final_this_time, (ulongest_t)finals,
+ (ulongest_t)object_count);
}
+ mps_arena_park(arena);
mps_ap_destroy(ap);
mps_root_destroy(mps_root);
mps_pool_destroy(amc);
@@ -262,7 +266,8 @@ int main(int argc, char *argv[])
mps_arena_t arena;
mps_thr_t thread;
void *r;
- testlib_unused(argc);
+
+ testlib_init(argc, argv);
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
"arena_create\n");
@@ -278,7 +283,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/failover.c b/code/failover.c
new file mode 100644
index 0000000000..750c6b1b17
--- /dev/null
+++ b/code/failover.c
@@ -0,0 +1,363 @@
+/* failover.c: FAILOVER IMPLEMENTATION
+ *
+ * $Id$
+ * Copyright (c) 2014 Ravenbrook Limited. See end of file for license.
+ *
+ * .design:
+ */
+
+#include "failover.h"
+#include "mpm.h"
+#include "range.h"
+
+SRCID(failover, "$Id$");
+
+
+#define failoverOfLand(land) PARENT(FailoverStruct, landStruct, land)
+
+
+ARG_DEFINE_KEY(failover_primary, Pointer);
+ARG_DEFINE_KEY(failover_secondary, Pointer);
+
+
+Bool FailoverCheck(Failover fo)
+{
+ CHECKS(Failover, fo);
+ CHECKD(Land, &fo->landStruct);
+ CHECKD(Land, fo->primary);
+ CHECKD(Land, fo->secondary);
+ return TRUE;
+}
+
+
+static Res failoverInit(Land land, ArgList args)
+{
+ Failover fo;
+ LandClass super;
+ Land primary, secondary;
+ ArgStruct arg;
+ Res res;
+
+ AVERT(Land, land);
+ super = LAND_SUPERCLASS(FailoverLandClass);
+ res = (*super->init)(land, args);
+ if (res != ResOK)
+ return res;
+
+ ArgRequire(&arg, args, FailoverPrimary);
+ primary = arg.val.p;
+ ArgRequire(&arg, args, FailoverSecondary);
+ secondary = arg.val.p;
+
+ fo = failoverOfLand(land);
+ fo->primary = primary;
+ fo->secondary = secondary;
+ fo->sig = FailoverSig;
+ AVERT(Failover, fo);
+ return ResOK;
+}
+
+
+static void failoverFinish(Land land)
+{
+ Failover fo;
+
+ AVERT(Land, land);
+ fo = failoverOfLand(land);
+ AVERT(Failover, fo);
+
+ fo->sig = SigInvalid;
+}
+
+
+static Size failoverSize(Land land)
+{
+ Failover fo;
+
+ AVERT(Land, land);
+ fo = failoverOfLand(land);
+ AVERT(Failover, fo);
+
+ return LandSize(fo->primary) + LandSize(fo->secondary);
+}
+
+
+static Res failoverInsert(Range rangeReturn, Land land, Range range)
+{
+ Failover fo;
+ Res res;
+
+ AVER(rangeReturn != NULL);
+ AVERT(Land, land);
+ fo = failoverOfLand(land);
+ AVERT(Failover, fo);
+ AVERT(Range, range);
+
+ /* Provide more opportunities for coalescence. See
+ * .
+ */
+ (void)LandFlush(fo->primary, fo->secondary);
+
+ res = LandInsert(rangeReturn, fo->primary, range);
+ if (res != ResOK && res != ResFAIL)
+ res = LandInsert(rangeReturn, fo->secondary, range);
+
+ return res;
+}
+
+
+static Res failoverDelete(Range rangeReturn, Land land, Range range)
+{
+ Failover fo;
+ Res res;
+ RangeStruct oldRange, dummyRange, left, right;
+
+ AVER(rangeReturn != NULL);
+ AVERT(Land, land);
+ fo = failoverOfLand(land);
+ AVERT(Failover, fo);
+ AVERT(Range, range);
+
+ /* Prefer efficient search in the primary. See
+ * .
+ */
+ (void)LandFlush(fo->primary, fo->secondary);
+
+ res = LandDelete(&oldRange, fo->primary, range);
+
+ if (res == ResFAIL) {
+ /* Range not found in primary: try secondary. */
+ return LandDelete(rangeReturn, fo->secondary, range);
+ } else if (res != ResOK) {
+ /* Range was found in primary, but couldn't be deleted. The only
+ * case we expect to encounter here is the case where the primary
+ * is out of memory. (In particular, we don't handle the case of a
+ * CBS returning ResLIMIT because its block pool has been
+ * configured not to automatically extend itself.)
+ */
+ AVER(ResIsAllocFailure(res));
+
+ /* Delete the whole of oldRange, and re-insert the fragments
+ * (which might end up in the secondary). See
+ * .
+ */
+ res = LandDelete(&dummyRange, fo->primary, &oldRange);
+ if (res != ResOK)
+ return res;
+
+ AVER(RangesEqual(&oldRange, &dummyRange));
+ RangeInit(&left, RangeBase(&oldRange), RangeBase(range));
+ if (!RangeIsEmpty(&left)) {
+ /* Don't call LandInsert(..., land, ...) here: that would be
+ * re-entrant and fail the landEnter check. */
+ res = LandInsert(&dummyRange, fo->primary, &left);
+ if (res != ResOK) {
+ /* The range was successful deleted from the primary above. */
+ AVER(res != ResFAIL);
+ res = LandInsert(&dummyRange, fo->secondary, &left);
+ AVER(res == ResOK);
+ }
+ }
+ RangeInit(&right, RangeLimit(range), RangeLimit(&oldRange));
+ if (!RangeIsEmpty(&right)) {
+ res = LandInsert(&dummyRange, fo->primary, &right);
+ if (res != ResOK) {
+ /* The range was successful deleted from the primary above. */
+ AVER(res != ResFAIL);
+ res = LandInsert(&dummyRange, fo->secondary, &right);
+ AVER(res == ResOK);
+ }
+ }
+ }
+ if (res == ResOK) {
+ AVER(RangesNest(&oldRange, range));
+ RangeCopy(rangeReturn, &oldRange);
+ }
+ return res;
+}
+
+
+static Bool failoverIterate(Land land, LandVisitor visitor, void *closureP, Size closureS)
+{
+ Failover fo;
+
+ AVERT(Land, land);
+ fo = failoverOfLand(land);
+ AVERT(Failover, fo);
+ AVER(visitor != NULL);
+
+ return LandIterate(fo->primary, visitor, closureP, closureS)
+ && LandIterate(fo->secondary, visitor, closureP, closureS);
+}
+
+
+static Bool failoverFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
+{
+ Failover fo;
+
+ AVER(rangeReturn != NULL);
+ AVER(oldRangeReturn != NULL);
+ AVERT(Land, land);
+ fo = failoverOfLand(land);
+ AVERT(Failover, fo);
+ AVERT(FindDelete, findDelete);
+
+ /* See . */
+ (void)LandFlush(fo->primary, fo->secondary);
+
+ return LandFindFirst(rangeReturn, oldRangeReturn, fo->primary, size, findDelete)
+ || LandFindFirst(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete);
+}
+
+
+static Bool failoverFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
+{
+ Failover fo;
+
+ AVER(rangeReturn != NULL);
+ AVER(oldRangeReturn != NULL);
+ AVERT(Land, land);
+ fo = failoverOfLand(land);
+ AVERT(Failover, fo);
+ AVERT(FindDelete, findDelete);
+
+ /* See . */
+ (void)LandFlush(fo->primary, fo->secondary);
+
+ return LandFindLast(rangeReturn, oldRangeReturn, fo->primary, size, findDelete)
+ || LandFindLast(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete);
+}
+
+
+static Bool failoverFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
+{
+ Failover fo;
+
+ AVER(rangeReturn != NULL);
+ AVER(oldRangeReturn != NULL);
+ AVERT(Land, land);
+ fo = failoverOfLand(land);
+ AVERT(Failover, fo);
+ AVERT(FindDelete, findDelete);
+
+ /* See . */
+ (void)LandFlush(fo->primary, fo->secondary);
+
+ return LandFindLargest(rangeReturn, oldRangeReturn, fo->primary, size, findDelete)
+ || LandFindLargest(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete);
+}
+
+
+static Bool failoverFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high)
+{
+ Failover fo;
+ Bool found = FALSE;
+ Res res;
+
+ AVER(FALSE); /* TODO: this code is completely untested! */
+ AVER(foundReturn != NULL);
+ AVER(rangeReturn != NULL);
+ AVER(oldRangeReturn != NULL);
+ AVERT(Land, land);
+ fo = failoverOfLand(land);
+ AVERT(Failover, fo);
+ /* AVERT(ZoneSet, zoneSet); */
+ AVERT(Bool, high);
+
+ /* See . */
+ (void)LandFlush(fo->primary, fo->secondary);
+
+ res = LandFindInZones(&found, rangeReturn, oldRangeReturn, fo->primary, size, zoneSet, high);
+ if (res != ResOK || !found)
+ res = LandFindInZones(&found, rangeReturn, oldRangeReturn, fo->secondary, size, zoneSet, high);
+
+ *foundReturn = found;
+ return res;
+}
+
+
+static Res failoverDescribe(Land land, mps_lib_FILE *stream, Count depth)
+{
+ Failover fo;
+ Res res;
+
+ if (!TESTT(Land, land))
+ return ResFAIL;
+ fo = failoverOfLand(land);
+ if (!TESTT(Failover, fo))
+ return ResFAIL;
+ if (stream == NULL)
+ return ResFAIL;
+
+ res = WriteF(stream, depth,
+ "Failover $P {\n", (WriteFP)fo,
+ " primary = $P ($S)\n", (WriteFP)fo->primary,
+ (WriteFS)fo->primary->class->name,
+ " secondary = $P ($S)\n", (WriteFP)fo->secondary,
+ (WriteFS)fo->secondary->class->name,
+ "}\n", NULL);
+
+ return res;
+}
+
+
+DEFINE_LAND_CLASS(FailoverLandClass, class)
+{
+ INHERIT_CLASS(class, LandClass);
+ class->name = "FAILOVER";
+ class->size = sizeof(FailoverStruct);
+ class->init = failoverInit;
+ class->finish = failoverFinish;
+ class->sizeMethod = failoverSize;
+ class->insert = failoverInsert;
+ class->delete = failoverDelete;
+ class->iterate = failoverIterate;
+ class->findFirst = failoverFindFirst;
+ class->findLast = failoverFindLast;
+ class->findLargest = failoverFindLargest;
+ class->findInZones = failoverFindInZones;
+ class->describe = failoverDescribe;
+ AVERT(LandClass, class);
+}
+
+
+/* C. COPYRIGHT AND LICENSE
+ *
+ * Copyright (C) 2014 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.
+ */
diff --git a/code/mpsw3.h b/code/failover.h
similarity index 69%
rename from code/mpsw3.h
rename to code/failover.h
index 2f543ddc0f..3676bade10 100644
--- a/code/mpsw3.h
+++ b/code/failover.h
@@ -1,45 +1,37 @@
-/* mpsw3.h: RAVENBROOK MEMORY POOL SYSTEM C INTERFACE, WINDOWS PART
+/* failover.h: FAILOVER ALLOCATOR INTERFACE
*
* $Id$
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2014 Ravenbrook Limited. See end of file for license.
*
- * .readership: customers, MPS developers.
- * .sources: .
+ * .source: .
*/
-#ifndef mpsw3_h
-#define mpsw3_h
+#ifndef failover_h
+#define failover_h
-#include "mps.h" /* needed for mps_tramp_t */
-#include /* needed for SEH filter */
+#include "mpmtypes.h"
+typedef struct FailoverStruct *Failover;
-extern LONG mps_SEH_filter(LPEXCEPTION_POINTERS, void **, size_t *);
-extern void mps_SEH_handler(void *, size_t);
+#define FailoverLand(fo) (&(fo)->landStruct)
+extern Bool FailoverCheck(Failover failover);
-#define mps_tramp(r_o, f, p, s) \
- MPS_BEGIN \
- void **_r_o = (r_o); \
- mps_tramp_t _f = (f); \
- void *_p = (p); \
- size_t _s = (s); \
- void *_hp = NULL; size_t _hs = 0; \
- __try { \
- *_r_o = (*_f)(_p, _s); \
- } __except(mps_SEH_filter(GetExceptionInformation(), \
- &_hp, &_hs)) { \
- mps_SEH_handler(_hp, _hs); \
- } \
- MPS_END
+extern LandClass FailoverLandClassGet(void);
+extern const struct mps_key_s _mps_key_failover_primary;
+#define FailoverPrimary (&_mps_key_failover_primary)
+#define FailoverPrimary_FIELD p
+extern const struct mps_key_s _mps_key_failover_secondary;
+#define FailoverSecondary (&_mps_key_failover_secondary)
+#define FailoverSecondary_FIELD p
-#endif /* mpsw3_h */
+#endif /* failover.h */
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/fbmtest.c b/code/fbmtest.c
index 954a647dfd..f5aa6831db 100644
--- a/code/fbmtest.c
+++ b/code/fbmtest.c
@@ -1,7 +1,7 @@
/* fbmtest.c: FREE BLOCK MANAGEMENT TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* The MPS contains two free block management modules:
*
@@ -21,12 +21,9 @@
#include "mpm.h"
#include "mps.h"
#include "mpsavm.h"
-#include "mpstd.h"
#include "testlib.h"
-#include
-#include
-#include
+#include /* printf */
SRCID(fbmtest, "$Id$");
@@ -41,7 +38,7 @@ SRCID(fbmtest, "$Id$");
static Count NAllocateTried, NAllocateSucceeded, NDeallocateTried,
NDeallocateSucceeded;
-static int verbose = 0;
+static Bool verbose = FALSE;
typedef unsigned FBMType;
enum {
@@ -83,13 +80,15 @@ static Index (indexOfAddr)(FBMState state, Addr a)
static void describe(FBMState state) {
switch (state->type) {
case FBMTypeCBS:
- CBSDescribe(state->the.cbs, mps_lib_get_stdout());
+ die(CBSDescribe(state->the.cbs, mps_lib_get_stdout(), 0),
+ "CBSDescribe");
break;
case FBMTypeFreelist:
- FreelistDescribe(state->the.fl, mps_lib_get_stdout());
+ die(FreelistDescribe(state->the.fl, mps_lib_get_stdout(), 0),
+ "FreelistDescribe");
break;
default:
- fail();
+ cdie(0, "invalid state->type");
break;
}
}
@@ -100,6 +99,7 @@ static Bool checkCallback(Range range, void *closureP, Size closureS)
Addr base, limit;
CheckFBMClosure cl = (CheckFBMClosure)closureP;
+ AVER(closureS == UNUSED_SIZE);
UNUSED(closureS);
Insist(cl != NULL);
@@ -151,13 +151,13 @@ static void check(FBMState state)
switch (state->type) {
case FBMTypeCBS:
- CBSIterate(state->the.cbs, checkCBSCallback, (void *)&closure, 0);
+ CBSIterate(state->the.cbs, checkCBSCallback, &closure, UNUSED_SIZE);
break;
case FBMTypeFreelist:
- FreelistIterate(state->the.fl, checkFLCallback, (void *)&closure, 0);
+ FreelistIterate(state->the.fl, checkFLCallback, &closure, UNUSED_SIZE);
break;
default:
- fail();
+ cdie(0, "invalid state->type");
return;
}
@@ -311,7 +311,7 @@ static void allocate(FBMState state, Addr base, Addr limit)
res = FreelistDelete(&oldRange, state->the.fl, &range);
break;
default:
- fail();
+ cdie(0, "invalid state->type");
return;
}
@@ -387,7 +387,7 @@ static void deallocate(FBMState state, Addr base, Addr limit)
res = FreelistInsert(&freeRange, state->the.fl, &range);
break;
default:
- fail();
+ cdie(0, "invalid state->type");
return;
}
@@ -432,20 +432,23 @@ static void find(FBMState state, Size size, Bool high, FindDelete findDelete)
remainderLimit = origLimit = addrOfIndex(state, expectedLimit);
switch(findDelete) {
- case FindDeleteNONE: {
+ case FindDeleteNONE:
/* do nothing */
- } break;
- case FindDeleteENTIRE: {
+ break;
+ case FindDeleteENTIRE:
remainderBase = remainderLimit;
- } break;
- case FindDeleteLOW: {
+ break;
+ case FindDeleteLOW:
expectedLimit = expectedBase + size;
remainderBase = addrOfIndex(state, expectedLimit);
- } break;
- case FindDeleteHIGH: {
+ break;
+ case FindDeleteHIGH:
expectedBase = expectedLimit - size;
remainderLimit = addrOfIndex(state, expectedBase);
- } break;
+ break;
+ default:
+ cdie(0, "invalid findDelete");
+ break;
}
if (findDelete != FindDeleteNONE) {
@@ -467,7 +470,7 @@ static void find(FBMState state, Size size, Bool high, FindDelete findDelete)
(&foundRange, &oldRange, state->the.fl, size * state->align, findDelete);
break;
default:
- fail();
+ cdie(0, "invalid state->type");
return;
}
@@ -528,9 +531,7 @@ static void test(FBMState state, unsigned n) {
size = fbmRnd(ArraySize / 10) + 1;
high = fbmRnd(2) ? TRUE : FALSE;
switch(fbmRnd(6)) {
- case 0:
- case 1:
- case 2: findDelete = FindDeleteNONE; break;
+ default: findDelete = FindDeleteNONE; break;
case 3: findDelete = FindDeleteLOW; break;
case 4: findDelete = FindDeleteHIGH; break;
case 5: findDelete = FindDeleteENTIRE; break;
@@ -538,11 +539,13 @@ static void test(FBMState state, unsigned n) {
find(state, size, high, findDelete);
break;
default:
- fail();
+ cdie(0, "invalid state->type");
return;
}
if ((i + 1) % 1000 == 0)
check(state);
+ if (i == 100)
+ describe(state);
}
}
@@ -560,8 +563,8 @@ extern int main(int argc, char *argv[])
CBSStruct cbsStruct;
Align align;
- randomize(argc, argv);
- align = (1 << rnd() % 4) * MPS_PF_ALIGN;
+ testlib_init(argc, argv);
+ align = sizeof(void *) << (rnd() % 4);
NAllocateTried = NAllocateSucceeded = NDeallocateTried =
NDeallocateSucceeded = 0;
@@ -585,7 +588,8 @@ extern int main(int argc, char *argv[])
(char *)dummyBlock + ArraySize);
}
- die((mps_res_t)CBSInit(arena, &cbsStruct, arena, align, TRUE, mps_args_none),
+ die((mps_res_t)CBSInit(&cbsStruct, arena, arena, align,
+ /* fastFind */ TRUE, /* zoned */ FALSE, mps_args_none),
"failed to initialise CBS");
state.type = FBMTypeCBS;
state.align = align;
@@ -604,10 +608,14 @@ extern int main(int argc, char *argv[])
mps_arena_destroy(arena);
- printf("\nNumber of allocations attempted: %ld\n", NAllocateTried);
- printf("Number of allocations succeeded: %ld\n", NAllocateSucceeded);
- printf("Number of deallocations attempted: %ld\n", NDeallocateTried);
- printf("Number of deallocations succeeded: %ld\n", NDeallocateSucceeded);
+ printf("\nNumber of allocations attempted: %"PRIuLONGEST"\n",
+ (ulongest_t)NAllocateTried);
+ printf("Number of allocations succeeded: %"PRIuLONGEST"\n",
+ (ulongest_t)NAllocateSucceeded);
+ printf("Number of deallocations attempted: %"PRIuLONGEST"\n",
+ (ulongest_t)NDeallocateTried);
+ printf("Number of deallocations succeeded: %"PRIuLONGEST"\n",
+ (ulongest_t)NDeallocateSucceeded);
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
return 0;
}
@@ -615,7 +623,7 @@ extern int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/finalcv.c b/code/finalcv.c
index 8236df4279..2be7f08373 100644
--- a/code/finalcv.c
+++ b/code/finalcv.c
@@ -1,7 +1,7 @@
/* finalcv.c: FINALIZATION COVERAGE TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (C) 2002 Global Graphics Software.
*
* DESIGN
@@ -18,18 +18,20 @@
* This code was created by first copying
*/
-#include "testlib.h"
-#include "mpslib.h"
-#include "mps.h"
-#include "mpscamc.h"
-#include "mpsavm.h"
#include "fmtdy.h"
#include "fmtdytst.h"
+#include "mpm.h"
+#include "mps.h"
+#include "mpsavm.h"
+#include "mpscamc.h"
+#include "mpscams.h"
+#include "mpscawl.h"
+#include "mpsclo.h"
+#include "mpslib.h"
#include "mpstd.h"
-#ifdef MPS_OS_W3
-#include "mpsw3.h"
-#endif
-#include
+#include "testlib.h"
+
+#include /* printf */
#define testArenaSIZE ((size_t)16<<20)
@@ -38,6 +40,7 @@
#define finalizationRATE 6
#define gcINTERVAL ((size_t)150 * 1024)
#define collectionCOUNT 3
+#define messageCOUNT 3
/* 3 words: wrapper | vector-len | first-slot */
#define vectorSIZE (3*sizeof(mps_word_t))
@@ -96,35 +99,37 @@ enum {
};
-static void *test(void *arg, size_t s)
+static void test(mps_arena_t arena, mps_pool_class_t pool_class)
{
- unsigned i; /* index */
+ size_t i; /* index */
mps_ap_t ap;
mps_fmt_t fmt;
mps_chain_t chain;
- mps_pool_t amc;
+ mps_pool_t pool;
mps_res_t e;
mps_root_t mps_root[2];
mps_addr_t nullref = NULL;
int state[rootCOUNT];
- mps_arena_t arena;
- void *p = NULL;
mps_message_t message;
+ size_t messages = 0;
+ void *p;
- arena = (mps_arena_t)arg;
- (void)s;
+ printf("---- finalcv: pool class %s ----\n", pool_class->name);
die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n");
die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create");
- die(mps_pool_create(&amc, arena, mps_class_amc(), fmt, chain),
- "pool_create amc\n");
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
+ MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt);
+ die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create\n");
+ } MPS_ARGS_END(args);
die(mps_root_create_table(&mps_root[0], arena, mps_rank_exact(), (mps_rm_t)0,
root, (size_t)rootCOUNT),
"root_create\n");
die(mps_root_create_table(&mps_root[1], arena, mps_rank_exact(), (mps_rm_t)0,
&p, (size_t)1),
"root_create\n");
- die(mps_ap_create(&ap, amc, mps_rank_exact()), "ap_create\n");
+ die(mps_ap_create(&ap, pool, mps_rank_exact()), "ap_create\n");
/* Make registered-for-finalization objects. */
/* */
@@ -146,7 +151,7 @@ static void *test(void *arg, size_t s)
mps_message_type_enable(arena, mps_message_type_finalization());
/* */
- while (mps_collections(arena) < collectionCOUNT) {
+ while (messages < messageCOUNT && mps_collections(arena) < collectionCOUNT) {
/* Perhaps cause (minor) collection */
churn(ap);
@@ -186,7 +191,8 @@ static void *test(void *arg, size_t s)
mps_message_finalization_ref(&objaddr, arena, message);
obj = objaddr;
objind = dylan_int_int(obj[vectorSLOT]);
- printf("Finalizing: object %lu at %p\n", objind, objaddr);
+ printf("Finalizing: object %"PRIuLONGEST" at %p\n",
+ (ulongest_t)objind, objaddr);
/* */
cdie(root[objind] == NULL, "finalized live");
cdie(state[objind] == finalizableSTATE, "finalized dead");
@@ -195,36 +201,34 @@ static void *test(void *arg, size_t s)
if (rnd() % 2 == 0)
root[objind] = objaddr;
mps_message_discard(arena, message);
+ ++ messages;
}
}
- /* @@@@ missing */
-
mps_ap_destroy(ap);
mps_root_destroy(mps_root[1]);
mps_root_destroy(mps_root[0]);
- mps_pool_destroy(amc);
+ mps_pool_destroy(pool);
mps_chain_destroy(chain);
mps_fmt_destroy(fmt);
-
- return NULL;
}
int main(int argc, char *argv[])
{
mps_arena_t arena;
- mps_thr_t thread;
- void *r;
- randomize(argc, argv);
- mps_lib_assert_fail_install(assert_die);
+ testlib_init(argc, argv);
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
"arena_create\n");
- die(mps_thread_reg(&thread, arena), "thread_reg\n");
- mps_tramp(&r, test, arena, 0);
- mps_thread_dereg(thread);
+
+ test(arena, mps_class_amc());
+ test(arena, mps_class_amcz());
+ test(arena, mps_class_awl());
+ test(arena, mps_class_ams());
+ test(arena, mps_class_lo());
+
mps_arena_destroy(arena);
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
@@ -234,7 +238,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/finaltest.c b/code/finaltest.c
index f5ef8a9a99..9f3ef13b96 100644
--- a/code/finaltest.c
+++ b/code/finaltest.c
@@ -1,11 +1,25 @@
/* finaltest.c: LARGE-SCALE FINALIZATION TEST
*
* $Id$
- * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (C) 2002 Global Graphics Software.
*
* 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,30 +30,31 @@
* This code was created by first copying
*/
+#include "mpm.h"
#include "testlib.h"
#include "mpslib.h"
#include "mps.h"
#include "mpscamc.h"
+#include "mpscams.h"
+#include "mpscawl.h"
+#include "mpsclo.h"
#include "mpsavm.h"
#include "fmtdy.h"
#include "fmtdytst.h"
#include "mpstd.h"
-#ifdef MPS_OS_W3
-#include "mpsw3.h"
-#endif
-#include
+
+#include /* fflush, printf, stdout */
+
+enum {
+ ModePARK, /* .mode.park */
+ ModePOLL /* .mode.poll */
+};
#define testArenaSIZE ((size_t)16<<20)
#define rootCOUNT 20
-#define maxtreeDEPTH 12
+#define maxtreeDEPTH 9
#define collectionCOUNT 10
-#define genCOUNT 2
-
-/* testChain -- generation parameters for the test */
-
-static mps_gen_param_s testChain[genCOUNT] = {
- { 150, 0.85 }, { 170, 0.45 } };
/* global object counter */
@@ -77,7 +92,7 @@ static void register_numbered_tree(mps_word_t tree, mps_arena_t arena)
/* don't finalize ints */
if ((tree & 1) == 0) {
mps_addr_t tree_ref = (mps_addr_t)tree;
- mps_finalize(arena, &tree_ref);
+ die(mps_finalize(arena, &tree_ref), "mps_finalize");
register_numbered_tree(DYLAN_VECTOR_SLOT(tree, 0), arena);
register_numbered_tree(DYLAN_VECTOR_SLOT(tree, 1), arena);
}
@@ -117,126 +132,130 @@ static void register_indirect_tree(mps_word_t tree, mps_arena_t arena)
if ((tree & 1) == 0) {
mps_word_t indirect = DYLAN_VECTOR_SLOT(tree,2);
mps_addr_t indirect_ref = (mps_addr_t)indirect;
- mps_finalize(arena, &indirect_ref);
+ die(mps_finalize(arena, &indirect_ref), "mps_finalize");
register_indirect_tree(DYLAN_VECTOR_SLOT(tree, 0), arena);
register_indirect_tree(DYLAN_VECTOR_SLOT(tree, 1), arena);
}
}
-
static void *root[rootCOUNT];
-static void *test(void *arg, size_t s)
+static void test_trees(int mode, const char *name, mps_arena_t arena,
+ mps_pool_t pool, mps_ap_t ap,
+ mps_word_t (*make)(mps_word_t, mps_ap_t),
+ void (*reg)(mps_word_t, mps_arena_t))
{
- mps_ap_t ap;
- mps_fmt_t fmt;
- mps_chain_t chain;
- mps_word_t finals;
- mps_pool_t amc;
- mps_root_t mps_root;
- mps_arena_t arena;
- mps_message_t message;
+ size_t collections = 0;
+ size_t finals = 0;
size_t i;
+ int object_alloc;
- arena = (mps_arena_t)arg;
- (void)s;
-
- die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n");
- die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create");
- die(mps_pool_create(&amc, arena, mps_class_amc(), fmt, chain),
- "pool_create amc\n");
- die(mps_root_create_table(&mps_root, arena, mps_rank_exact(), (mps_rm_t)0,
- root, (size_t)rootCOUNT),
- "root_create\n");
- die(mps_ap_create(&ap, amc, mps_rank_exact()), "ap_create\n");
-
- mps_message_type_enable(arena, mps_message_type_finalization());
+ object_count = 0;
+ printf("---- Mode %s, pool class %s, %s trees ----\n",
+ mode == ModePARK ? "PARK" : "POLL",
+ pool->class->name, name);
mps_arena_park(arena);
- object_count = 0;
-
- printf("Making some finalized trees of objects.\n");
/* make some trees */
for(i = 0; i < rootCOUNT; ++i) {
- root[i] = (void *)make_numbered_tree(maxtreeDEPTH, ap);
- register_numbered_tree((mps_word_t)root[i], arena);
+ root[i] = (void *)(*make)(maxtreeDEPTH, ap);
+ (*reg)((mps_word_t)root[i], arena);
}
- printf("Losing all pointers to the trees.\n");
/* clean out the roots */
for(i = 0; i < rootCOUNT; ++i) {
- root[i] = 0;
+ root[i] = 0;
}
- finals = 0;
-
- while ((finals < object_count) &&
- (mps_collections(arena) < collectionCOUNT)) {
- mps_word_t final_this_time = 0;
- printf("Collecting...");
- fflush(stdout);
- die(mps_arena_collect(arena), "collect");
- printf(" Done.\n");
- while (mps_message_poll(arena)) {
- mps_addr_t objaddr;
- cdie(mps_message_get(&message, arena,
- mps_message_type_finalization()),
- "get");
- mps_message_finalization_ref(&objaddr, arena, message);
- mps_message_discard(arena, message);
- ++ final_this_time;
- }
- finals += final_this_time;
- printf("%lu objects finalized: total %lu of %lu\n",
- final_this_time, finals, object_count);
+ while (finals < object_count && collections < collectionCOUNT) {
+ mps_word_t final_this_time = 0;
+ 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++);
+ printf(" Done.\n");
+ break;
+ }
+ ++ collections;
+ {
+ size_t live_size = (object_count - finals) * sizeof(void *) * 3;
+ size_t alloc_size = mps_pool_total_size(pool) - mps_pool_free_size(pool);
+ Insist(live_size <= alloc_size);
+ }
+ while (mps_message_poll(arena)) {
+ mps_message_t message;
+ mps_addr_t objaddr;
+ cdie(mps_message_get(&message, arena, mps_message_type_finalization()),
+ "message_get");
+ mps_message_finalization_ref(&objaddr, arena, message);
+ mps_message_discard(arena, message);
+ ++ final_this_time;
+ }
+ finals += final_this_time;
+ printf("%"PRIuLONGEST" objects finalized: total %"PRIuLONGEST
+ " of %"PRIuLONGEST"\n", (ulongest_t)final_this_time,
+ (ulongest_t)finals, (ulongest_t)object_count);
}
+ if (finals != object_count)
+ error("Not all objects were finalized for %s in mode %s.",
+ BufferOfAP(ap)->pool->class->name,
+ mode == ModePOLL ? "POLL" : "PARK");
+}
- object_count = 0;
-
- printf("Making some indirectly finalized trees of objects.\n");
- /* make some trees */
- for(i = 0; i < rootCOUNT; ++i) {
- root[i] = (void *)make_indirect_tree(maxtreeDEPTH, ap);
- register_indirect_tree((mps_word_t)root[i], arena);
- }
+static void test_pool(int mode, mps_arena_t arena, mps_chain_t chain,
+ mps_pool_class_t pool_class)
+{
+ mps_ap_t ap;
+ mps_fmt_t fmt;
+ mps_pool_t pool;
+ mps_root_t mps_root;
- printf("Losing all pointers to the trees.\n");
- /* clean out the roots */
- for(i = 0; i < rootCOUNT; ++i) {
- root[i] = 0;
- }
+ die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n");
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt);
+ if (mode == ModePOLL) {
+ MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
+ MPS_ARGS_ADD(args, MPS_KEY_GEN, 1);
+ }
+ die(mps_pool_create_k(&pool, arena, pool_class, args),
+ "pool_create\n");
+ } MPS_ARGS_END(args);
+ die(mps_root_create_table(&mps_root, arena, mps_rank_exact(), (mps_rm_t)0,
+ root, (size_t)rootCOUNT),
+ "root_create\n");
+ die(mps_ap_create(&ap, pool, mps_rank_exact()), "ap_create\n");
- finals = 0;
-
- while ((finals < object_count) &&
- (mps_collections(arena) < collectionCOUNT)) {
- mps_word_t final_this_time = 0;
- printf("Collecting...");
- fflush(stdout);
- die(mps_arena_collect(arena), "collect");
- printf(" Done.\n");
- while (mps_message_poll(arena)) {
- mps_addr_t objaddr;
- cdie(mps_message_get(&message, arena,
- mps_message_type_finalization()),
- "get");
- mps_message_finalization_ref(&objaddr, arena, message);
- mps_message_discard(arena, message);
- ++ final_this_time;
- }
- finals += final_this_time;
- printf("%lu objects finalized: total %lu of %lu\n",
- final_this_time, finals, object_count);
- }
+ test_trees(mode, "numbered", arena, pool, ap, make_numbered_tree,
+ register_numbered_tree);
+ test_trees(mode, "indirect", arena, pool, ap, make_indirect_tree,
+ register_indirect_tree);
mps_ap_destroy(ap);
mps_root_destroy(mps_root);
- mps_pool_destroy(amc);
- mps_chain_destroy(chain);
+ 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_awl());
+ test_pool(mode, arena, chain, mps_class_lo());
}
@@ -244,13 +263,28 @@ int main(int argc, char *argv[])
{
mps_arena_t arena;
mps_thr_t thread;
- void *r;
- testlib_unused(argc);
+ 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");
- mps_tramp(&r, test, arena, 0);
+ 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_mode(ModePOLL, arena, chain);
+ test_mode(ModePARK, arena, NULL);
+
+ mps_arena_park(arena);
+ mps_chain_destroy(chain);
mps_thread_dereg(thread);
mps_arena_destroy(arena);
@@ -261,7 +295,7 @@ int main(int argc, char *argv[])
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (c) 2001-2013 Ravenbrook Limited .
+ * Copyright (c) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/fmtdy.c b/code/fmtdy.c
index f622bfe128..0cb4adea11 100644
--- a/code/fmtdy.c
+++ b/code/fmtdy.c
@@ -1,7 +1,7 @@
/* fmtdy.c: DYLAN OBJECT FORMAT IMPLEMENTATION
*
* $Id$
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
* Portions copyright (c) 2002 Global Graphics Software.
*
* .readership: MPS developers, Dylan developers
@@ -69,10 +69,6 @@
/* MPS_END causes "constant conditional" warnings. */
#pragma warning(disable: 4127)
-/* windows.h causes warnings about "unreferenced inline function */
-/* has been removed". */
-#pragma warning(disable: 4514)
-
#endif /* _MSC_VER */
@@ -470,8 +466,8 @@ extern mps_res_t dylan_scan1(mps_ss_t mps_ss, mps_addr_t *object_io)
break;
case 1: /* stretchy non-traceable */
- notreached(); /* Not used by DylanWorks yet */
p += vt + 1;
+ notreached(); /* Not used by DylanWorks yet */
break;
case 2: /* non-stretchy traceable */
@@ -482,7 +478,6 @@ extern mps_res_t dylan_scan1(mps_ss_t mps_ss, mps_addr_t *object_io)
break;
case 3: /* stretchy traceable */
- notreached(); /* DW doesn't create them yet */
vl = *(mps_word_t *)p; /* vector length */
assert((vl & 3) == 1); /* check Dylan integer tag */
vl >>= 2; /* untag it */
@@ -490,21 +485,22 @@ extern mps_res_t dylan_scan1(mps_ss_t mps_ss, mps_addr_t *object_io)
res = dylan_scan_contig(mps_ss, p, p + vl);
if(res) return res;
p += vt; /* skip to end of whole vector */
+ notreached(); /* DW doesn't create them yet */
break;
case 4: /* non-word */
- es = (vh & 0xff) >> 3;
- vb = (vh >> 16) & 0xff;
+ es = (unsigned)(vh & 0xff) >> 3;
+ vb = (unsigned)((vh >> 16) & 0xff);
vt += vb;
p += NONWORD_LENGTH(vt, es);
break;
case 5: /* stretchy non-word */
- notreached(); /* DW doesn't create them yet */
- es = (vh & 0xff) >> 3;
- vb = (vh >> 16) & 0xff;
+ es = (unsigned)(vh & 0xff) >> 3;
+ vb = (unsigned)((vh >> 16) & 0xff);
vt += vb;
p += NONWORD_LENGTH(vt, es) + 1;
+ notreached(); /* DW doesn't create them yet */
break;
default:
@@ -521,10 +517,13 @@ static mps_res_t dylan_scan(mps_ss_t mps_ss,
mps_addr_t base, mps_addr_t limit)
{
mps_res_t res;
+ mps_addr_t prev = base;
while(base < limit) {
+ prev = base;
res = dylan_scan1(mps_ss, &base);
if(res) return res;
+ assert(prev < base);
}
assert(base == limit);
@@ -678,8 +677,8 @@ static mps_addr_t dylan_skip(mps_addr_t object)
if((vf & 6) == 4) /* non-word */
{
- es = (vh & 0xff) >> 3;
- vb = (vh >> 16) & 0xff;
+ es = (unsigned)(vh & 0xff) >> 3;
+ vb = (unsigned)((vh >> 16) & 0xff);
vt += vb;
p += NONWORD_LENGTH(vt, es);
}
@@ -721,6 +720,7 @@ static void dylan_fwd(mps_addr_t old, mps_addr_t new)
mps_addr_t limit;
assert(dylan_isfwd(old) == NULL);
+ assert((*(mps_word_t *)old & 3) == 0); /* mustn't forward padding objects */
assert(((mps_word_t)new & 3) == 0);
p = (mps_word_t *)old;
@@ -844,7 +844,7 @@ mps_res_t dylan_fmt_weak(mps_fmt_t *mps_fmt_o, mps_arena_t arena)
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/fmtdytst.c b/code/fmtdytst.c
index 790840f549..c797109870 100644
--- a/code/fmtdytst.c
+++ b/code/fmtdytst.c
@@ -1,7 +1,7 @@
/* fmtdytst.c: DYLAN FORMAT TEST CODE
*
* $Id$
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .readership: MPS developers, Dylan developers.
*/
@@ -17,12 +17,6 @@
#define unused(param) ((void)param)
-#ifdef MPS_BUILD_MV
-/* windows.h causes warnings about "unreferenced inline function */
-/* has been removed". */
-#pragma warning(disable: 4514)
-#endif /* MPS_BUILD_MV */
-
static mps_word_t *ww = NULL;
static mps_word_t *tvw;
@@ -79,8 +73,12 @@ mps_res_t dylan_make_wrappers(void)
* If the raw memory is large enough, initialises it to a dylan-vector,
* whose slots are initialised to either dylan-ints, or valid refs, at
* random.
- * Caller must supply an array of (at least 1) valid refs to copy, via
- * the "refs" and "nr_refs" arguments.
+ *
+ * Caller must supply an array of valid refs to copy, via the "refs"
+ * and "nr_refs" arguments. If "nr_refs" is 0, all slots are
+ * initialized to dylan-ints: this may be useful for making leaf
+ * objects.
+ *
* (Makes a pad if the raw memory is too small to hold a dylan-vector)
*/
@@ -106,7 +104,7 @@ mps_res_t dylan_init(mps_addr_t addr, size_t size,
for(i = 0; i < t; ++i) {
mps_word_t r = rnd();
- if(r & 1)
+ if(nr_refs == 0 || (r & 1))
p[2+i] = ((r & ~(mps_word_t)3) | 1); /* random int */
else
p[2+i] = (mps_word_t)refs[(r >> 1) % nr_refs]; /* random ptr */
@@ -222,7 +220,7 @@ mps_bool_t dylan_check(mps_addr_t addr)
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/fmtno.c b/code/fmtno.c
index 6c632fef06..05f84651bd 100644
--- a/code/fmtno.c
+++ b/code/fmtno.c
@@ -1,7 +1,7 @@
/* fmtno.c: NULL OBJECT FORMAT IMPLEMENTATION
*
* $Id$
- * Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* .readership: MPS developers
*/
@@ -17,22 +17,6 @@
#define notreached() assert(0)
#define unused(param) ((void)param)
-#ifdef MPS_BUILD_MV
-
-/* MSVC 2.0 generates a warning for unused(). */
-#ifdef _MSC_VER
-#if _MSC_VER < 1000
-#pragma warning(disable: 4705)
-#endif
-#else /* _MSC_VER */
-#error "Expected _MSC_VER to be defined for builder.mv"
-#endif /* _MSC_VER */
-
-/* windows.h causes warnings about "unreferenced inline function */
-/* has been removed". */
-#pragma warning(disable: 4514)
-
-#endif /* MPS_BUILD_MV */
#define ALIGN sizeof(mps_word_t)
@@ -137,7 +121,7 @@ mps_res_t no_fmt(mps_fmt_t *mps_fmt_o, mps_arena_t arena)
/* C. COPYRIGHT AND LICENSE
*
- * Copyright (C) 2001-2002 Ravenbrook Limited .
+ * Copyright (C) 2001-2014 Ravenbrook Limited .
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
diff --git a/code/fmtscheme.c b/code/fmtscheme.c
new file mode 100644
index 0000000000..24fa06db87
--- /dev/null
+++ b/code/fmtscheme.c
@@ -0,0 +1,500 @@
+/* fmtscheme.c: SCHEME OBJECT FORMAT IMPLEMENTATION
+ *
+ * $Id: //info.ravenbrook.com/project/mps/branch/2014-01-15/nailboard/code/fmtdy.c#1 $
+ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
+ */
+
+#include
+
+#include "fmtscheme.h"
+#include "testlib.h"
+
+
+/* special objects */
+
+static obj_t obj_true; /* #t, boolean true */
+static obj_t obj_false; /* #f, boolean false */
+
+
+/* MPS globals */
+
+mps_arena_t scheme_arena; /* the arena */
+mps_pool_t obj_pool; /* pool for ordinary Scheme objects */
+mps_ap_t obj_ap; /* allocation point used to allocate objects */
+
+
+/* make_* -- object constructors */
+
+#define ALIGNMENT sizeof(mps_word_t)
+
+/* Align size upwards to the next multiple of the word size. */
+#define ALIGN_WORD(size) \
+ (((size) + ALIGNMENT - 1) & ~(ALIGNMENT - 1))
+
+/* Align size upwards to the next multiple of the word size, and
+ * additionally ensure that it's big enough to store a forwarding
+ * pointer. Evaluates its argument twice. */
+#define ALIGN_OBJ(size) \
+ (ALIGN_WORD(size) >= ALIGN_WORD(sizeof(fwd_s)) \
+ ? ALIGN_WORD(size) \
+ : ALIGN_WORD(sizeof(fwd_s)))
+
+obj_t scheme_make_bool(int condition)
+{
+ return condition ? obj_true : obj_false;
+}
+
+obj_t scheme_make_pair(mps_ap_t ap, obj_t car, obj_t cdr)
+{
+ obj_t obj;
+ mps_addr_t addr;
+ size_t size = ALIGN_OBJ(sizeof(pair_s));
+ do {
+ mps_res_t res = mps_reserve(&addr, ap, size);
+ if (res != MPS_RES_OK) error("out of memory in make_pair");
+ obj = addr;
+ obj->pair.type = TYPE_PAIR;
+ CAR(obj) = car;
+ CDR(obj) = cdr;
+ } while(!mps_commit(ap, addr, size));
+ return obj;
+}
+
+obj_t scheme_make_integer(mps_ap_t ap, long integer)
+{
+ obj_t obj;
+ mps_addr_t addr;
+ size_t size = ALIGN_OBJ(sizeof(integer_s));
+ do {
+ mps_res_t res = mps_reserve(&addr, ap, size);
+ if (res != MPS_RES_OK) error("out of memory in make_integer");
+ obj = addr;
+ obj->integer.type = TYPE_INTEGER;
+ obj->integer.integer = integer;
+ } while(!mps_commit(ap, addr, size));
+ return obj;
+}
+
+obj_t scheme_make_symbol(mps_ap_t ap, size_t length, char string[])
+{
+ obj_t obj;
+ mps_addr_t addr;
+ size_t size = ALIGN_OBJ(offsetof(symbol_s, string) + length+1);
+ do {
+ mps_res_t res = mps_reserve(&addr, ap, size);
+ if (res != MPS_RES_OK) error("out of memory in make_symbol");
+ obj = addr;
+ obj->symbol.type = TYPE_SYMBOL;
+ obj->symbol.length = length;
+ memcpy(obj->symbol.string, string, length+1);
+ } while(!mps_commit(ap, addr, size));
+ return obj;
+}
+
+obj_t scheme_make_string(mps_ap_t ap, size_t length, char string[])
+{
+ obj_t obj;
+ mps_addr_t addr;
+ size_t size = ALIGN_OBJ(offsetof(string_s, string) + length+1);
+ do {
+ mps_res_t res = mps_reserve(&addr, ap, size);
+ if (res != MPS_RES_OK) error("out of memory in make_string");
+ obj = addr;
+ obj->string.type = TYPE_STRING;
+ obj->string.length = length;
+ if (string) memcpy(obj->string.string, string, length+1);
+ else memset(obj->string.string, 0, length+1);
+ } while(!mps_commit(ap, addr, size));
+ return obj;
+}
+
+obj_t scheme_make_special(mps_ap_t ap, char *string)
+{
+ obj_t obj;
+ mps_addr_t addr;
+ size_t size = ALIGN_OBJ(sizeof(special_s));
+ do {
+ mps_res_t res = mps_reserve(&addr, ap, size);
+ if (res != MPS_RES_OK) error("out of memory in make_special");
+ obj = addr;
+ obj->special.type = TYPE_SPECIAL;
+ obj->special.name = string;
+ } while(!mps_commit(ap, addr, size));
+ return obj;
+}
+
+obj_t scheme_make_operator(mps_ap_t ap, char *name,
+ entry_t entry, obj_t arguments,
+ obj_t body, obj_t env, obj_t op_env)
+{
+ obj_t obj;
+ mps_addr_t addr;
+ size_t size = ALIGN_OBJ(sizeof(operator_s));
+ do {
+ mps_res_t res = mps_reserve(&addr, ap, size);
+ if (res != MPS_RES_OK) error("out of memory in make_operator");
+ obj = addr;
+ obj->operator.type = TYPE_OPERATOR;
+ obj->operator.name = name;
+ obj->operator.entry = entry;
+ obj->operator.arguments = arguments;
+ obj->operator.body = body;
+ obj->operator.env = env;
+ obj->operator.op_env = op_env;
+ } while(!mps_commit(ap, addr, size));
+ return obj;
+}
+
+obj_t scheme_make_port(mps_ap_t ap, obj_t name, FILE *stream)
+{
+ mps_addr_t port_ref;
+ obj_t obj;
+ mps_addr_t addr;
+ size_t size = ALIGN_OBJ(sizeof(port_s));
+ do {
+ mps_res_t res = mps_reserve(&addr, ap, size);
+ if (res != MPS_RES_OK) error("out of memory in make_port");
+ obj = addr;
+ obj->port.type = TYPE_PORT;
+ obj->port.name = name;
+ obj->port.stream = stream;
+ } while(!mps_commit(ap, addr, size));
+ port_ref = obj;
+ mps_finalize(scheme_arena, &port_ref);
+ return obj;
+}
+
+obj_t scheme_make_character(mps_ap_t ap, char c)
+{
+ obj_t obj;
+ mps_addr_t addr;
+ size_t size = ALIGN_OBJ(sizeof(character_s));
+ do {
+ mps_res_t res = mps_reserve(&addr, ap, size);
+ if (res != MPS_RES_OK) error("out of memory in make_character");
+ obj = addr;
+ obj->character.type = TYPE_CHARACTER;
+ obj->character.c = c;
+ } while(!mps_commit(ap, addr, size));
+ return obj;
+}
+
+obj_t scheme_make_vector(mps_ap_t ap, size_t length, obj_t fill)
+{
+ obj_t obj;
+ mps_addr_t addr;
+ size_t size = ALIGN_OBJ(offsetof(vector_s, vector) + length * sizeof(obj_t));
+ do {
+ size_t i;
+ mps_res_t res = mps_reserve(&addr, ap, size);
+ if (res != MPS_RES_OK) error("out of memory in make_vector");
+ obj = addr;
+ obj->vector.type = TYPE_VECTOR;
+ obj->vector.length = length;
+ for(i = 0; i < length; ++i)
+ obj->vector.vector[i] = fill;
+ } while(!mps_commit(ap, addr, size));
+ return obj;
+}
+
+obj_t scheme_make_buckets(mps_ap_t ap, size_t length)
+{
+ obj_t obj;
+ mps_addr_t addr;
+ size_t size = ALIGN_OBJ(offsetof(buckets_s, bucket) + length * sizeof(obj->buckets.bucket[0]));
+ do {
+ size_t i;
+ mps_res_t res = mps_reserve(&addr, ap, size);
+ if (res != MPS_RES_OK) error("out of memory in make_buckets");
+ obj = addr;
+ obj->buckets.type = TYPE_BUCKETS;
+ obj->buckets.length = length;
+ obj->buckets.used = 0;
+ obj->buckets.deleted = 0;
+ for(i = 0; i < length; ++i) {
+ obj->buckets.bucket[i].key = NULL;
+ obj->buckets.bucket[i].value = NULL;
+ }
+ } while(!mps_commit(ap, addr, size));
+ return obj;
+}
+
+obj_t scheme_make_table(mps_ap_t ap, size_t length, hash_t hashf, cmp_t cmpf)
+{
+ obj_t obj;
+ mps_addr_t addr;
+ size_t l, size = ALIGN_OBJ(sizeof(table_s));
+ do {
+ mps_res_t res = mps_reserve(&addr, ap, size);
+ if (res != MPS_RES_OK) error("out of memory in make_table");
+ obj = addr;
+ obj->table.type = TYPE_TABLE;
+ obj->table.buckets = NULL;
+ } while(!mps_commit(ap, addr, size));
+ obj->table.hash = hashf;
+ obj->table.cmp = cmpf;
+ /* round up to next power of 2 */
+ for(l = 1; l < length; l *= 2);
+ obj->table.buckets = scheme_make_buckets(ap, l);
+ mps_ld_reset(&obj->table.ld, scheme_arena);
+ return obj;
+}
+
+
+/* MPS Format */
+
+static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
+{
+#define FIX(ref) \
+ do { \
+ mps_addr_t _addr = (ref); /* copy to local to avoid type pun */ \
+ mps_res_t res = MPS_FIX12(ss, &_addr); \
+ if (res != MPS_RES_OK) return res; \
+ (ref) = _addr; \
+ } while(0)
+
+ MPS_SCAN_BEGIN(ss) {
+ while (base < limit) {
+ obj_t obj = base;
+ switch (TYPE(obj)) {
+ case TYPE_PAIR:
+ case TYPE_PROMISE:
+ FIX(CAR(obj));
+ FIX(CDR(obj));
+ base = (char *)base + ALIGN_OBJ(sizeof(pair_s));
+ break;
+ case TYPE_INTEGER:
+ base = (char *)base + ALIGN_OBJ(sizeof(integer_s));
+ break;
+ case TYPE_SYMBOL:
+ base = (char *)base +
+ ALIGN_OBJ(offsetof(symbol_s, string) + obj->symbol.length + 1);
+ break;
+ case TYPE_SPECIAL:
+ base = (char *)base + ALIGN_OBJ(sizeof(special_s));
+ break;
+ case TYPE_OPERATOR:
+ FIX(obj->operator.arguments);
+ FIX(obj->operator.body);
+ FIX(obj->operator.env);
+ FIX(obj->operator.op_env);
+ base = (char *)base + ALIGN_OBJ(sizeof(operator_s));
+ break;
+ case TYPE_STRING:
+ base = (char *)base +
+ ALIGN_OBJ(offsetof(string_s, string) + obj->string.length + 1);
+ break;
+ case TYPE_PORT:
+ FIX(obj->port.name);
+ base = (char *)base + ALIGN_OBJ(sizeof(port_s));
+ break;
+ case TYPE_CHARACTER:
+ base = (char *)base + ALIGN_OBJ(sizeof(character_s));
+ break;
+ case TYPE_VECTOR:
+ {
+ size_t i;
+ for (i = 0; i < obj->vector.length; ++i)
+ FIX(obj->vector.vector[i]);
+ }
+ base = (char *)base +
+ ALIGN_OBJ(offsetof(vector_s, vector) +
+ obj->vector.length * sizeof(obj->vector.vector[0]));
+ break;
+ case TYPE_BUCKETS:
+ {
+ size_t i;
+ for (i = 0; i < obj->buckets.length; ++i) {
+ FIX(obj->buckets.bucket[i].key);
+ FIX(obj->buckets.bucket[i].value);
+ }
+ }
+ base = (char *)base +
+ ALIGN_OBJ(offsetof(buckets_s, bucket) +
+ obj->buckets.length * sizeof(obj->buckets.bucket[0]));
+ break;
+ case TYPE_TABLE:
+ FIX(obj->table.buckets);
+ base = (char *)base + ALIGN_OBJ(sizeof(table_s));
+ break;
+ case TYPE_FWD2:
+ base = (char *)base + ALIGN_WORD(sizeof(fwd2_s));
+ break;
+ case TYPE_FWD:
+ base = (char *)base + ALIGN_WORD(obj->fwd.size);
+ break;
+ case TYPE_PAD1:
+ base = (char *)base + ALIGN_WORD(sizeof(pad1_s));
+ break;
+ case TYPE_PAD:
+ base = (char *)base + ALIGN_WORD(obj->pad.size);
+ break;
+ default:
+ error("Unexpected object on the heap\n");
+ return MPS_RES_FAIL;
+ }
+ }
+ } MPS_SCAN_END(ss);
+ return MPS_RES_OK;
+}
+
+static mps_addr_t obj_skip(mps_addr_t base)
+{
+ obj_t obj = base;
+ switch (TYPE(obj)) {
+ case TYPE_PAIR:
+ case TYPE_PROMISE:
+ base = (char *)base + ALIGN_OBJ(sizeof(pair_s));
+ break;
+ case TYPE_INTEGER:
+ base = (char *)base + ALIGN_OBJ(sizeof(integer_s));
+ break;
+ case TYPE_SYMBOL:
+ base = (char *)base +
+ ALIGN_OBJ(offsetof(symbol_s, string) + obj->symbol.length + 1);
+ break;
+ case TYPE_SPECIAL:
+ base = (char *)base + ALIGN_OBJ(sizeof(special_s));
+ break;
+ case TYPE_OPERATOR:
+ base = (char *)base + ALIGN_OBJ(sizeof(operator_s));
+ break;
+ case TYPE_STRING:
+ base = (char *)base +
+ ALIGN_OBJ(offsetof(string_s, string) + obj->string.length + 1);
+ break;
+ case TYPE_PORT:
+ base = (char *)base + ALIGN_OBJ(sizeof(port_s));
+ break;
+ case TYPE_CHARACTER:
+ base = (char *)base + ALIGN_OBJ(sizeof(character_s));
+ break;
+ case TYPE_VECTOR:
+ base = (char *)base +
+ ALIGN_OBJ(offsetof(vector_s, vector) +
+ obj->vector.length * sizeof(obj->vector.vector[0]));
+ break;
+ case TYPE_BUCKETS:
+ base = (char *)base +
+ ALIGN_OBJ(offsetof(buckets_s, bucket) +
+ obj->buckets.length * sizeof(obj->buckets.bucket[0]));
+ break;
+ case TYPE_TABLE:
+ base = (char *)base + ALIGN_OBJ(sizeof(table_s));
+ break;
+ case TYPE_FWD2:
+ base = (char *)base + ALIGN_WORD(sizeof(fwd2_s));
+ break;
+ case TYPE_FWD:
+ base = (char *)base + ALIGN_WORD(obj->fwd.size);
+ break;
+ case TYPE_PAD:
+ base = (char *)base + ALIGN_WORD(obj->pad.size);
+ break;
+ case TYPE_PAD1:
+ base = (char *)base + ALIGN_WORD(sizeof(pad1_s));
+ break;
+ default:
+ error("Unexpected object on the heap\n");
+ return NULL;
+ }
+ return base;
+}
+
+static mps_addr_t obj_isfwd(mps_addr_t addr)
+{
+ obj_t obj = addr;
+ switch (TYPE(obj)) {
+ case TYPE_FWD2:
+ return obj->fwd2.fwd;
+ case TYPE_FWD:
+ return obj->fwd.fwd;
+ default:
+ return NULL;
+ }
+}
+
+static void obj_fwd(mps_addr_t old, mps_addr_t new)
+{
+ obj_t obj = old;
+ mps_addr_t limit = obj_skip(old);
+ size_t size = (size_t)((char *)limit - (char *)old);
+ cdie(size >= ALIGN_WORD(sizeof(fwd2_s)), "bad size in obj_fwd");
+ if (size == ALIGN_WORD(sizeof(fwd2_s))) {
+ TYPE(obj) = TYPE_FWD2;
+ obj->fwd2.fwd = new;
+ } else {
+ TYPE(obj) = TYPE_FWD;
+ obj->fwd.fwd = new;
+ obj->fwd.size = size;
+ }
+}
+
+static void obj_pad(mps_addr_t addr, size_t size)
+{
+ obj_t obj = addr;
+ cdie(size >= ALIGN_WORD(sizeof(pad1_s)), "bad size in obj_pad");
+ if (size == ALIGN_WORD(sizeof(pad1_s))) {
+ TYPE(obj) = TYPE_PAD1;
+ } else {
+ TYPE(obj) = TYPE_PAD;
+ obj->pad.size = size;
+ }
+}
+
+void scheme_fmt(mps_fmt_t *fmt)
+{
+ mps_res_t res;
+ MPS_ARGS_BEGIN(args) {
+ MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, ALIGNMENT);
+ MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, obj_scan);
+ MPS_ARGS_ADD(args, MPS_KEY_FMT_SKIP, obj_skip);
+ MPS_ARGS_ADD(args, MPS_KEY_FMT_FWD, obj_fwd);
+ MPS_ARGS_ADD(args, MPS_KEY_FMT_ISFWD, obj_isfwd);
+ MPS_ARGS_ADD(args, MPS_KEY_FMT_PAD, obj_pad);
+ res = mps_fmt_create_k(fmt, scheme_arena, args);
+ } MPS_ARGS_END(args);
+ if (res != MPS_RES_OK) error("Couldn't create obj format");
+}
+
+
+/* C. COPYRIGHT AND LICENSE
+ *
+ * Copyright (C) 2001-2014 Ravenbrook Limited