From 6bd64a230a510bc19323da127d622a19bfd43f63 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sun, 23 Jan 2022 15:25:47 +0000 Subject: [PATCH 1/3] Apply all changes from Configura to the public MPS. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These changes were originally implemented as custom work for Configura on the custom/cet/main branch. With permission from the customer (see e-mail https://info.ravenbrook.com/mail/2022/01/18/12-53-38/0/ from Göran Rydqvist) we apply them to the public MPS, subject to future work to integrate the custom documentation and interfaces into the public manual and headers. The custom interfaces are as follows: 1. The Transforms interface provides a mechanism to rewrite references throughout the MPS-managed heap; 2. The Arena extension/contraction callbacks provide a mechanism for the client to register stack decoding callbacks on Windows managed runtime; 3. The hash arrays flag for allocation points prevents the MPS from aggressively collecting address-based hash tables. --- code/comm.gmk | 11 + code/commpost.nmk | 3 - code/commpre.nmk | 8 + code/mps.c | 5 + code/mps.xcodeproj/project.pbxproj | 124 ++ code/mpscvm.h | 79 ++ code/mpsitr.c | 135 +++ code/mpstr.h | 66 ++ code/protw3.c | 7 +- code/testlib.h | 2 + code/trans.c | 397 +++++++ code/trans.h | 75 ++ code/ztfm.c | 1465 ++++++++++++++++++++++++ design/transform.txt | 126 ++ manual/source/custom/cet/extension.rst | 108 ++ manual/source/custom/cet/hasharray.rst | 61 + manual/source/custom/cet/index.rst | 11 + manual/source/custom/cet/transform.rst | 132 +++ manual/source/design/index.rst | 1 + manual/source/index.rst | 1 + procedure/release-build.rst | 4 +- tool/testcases.txt | 1 + 22 files changed, 2814 insertions(+), 8 deletions(-) create mode 100644 code/mpscvm.h create mode 100644 code/mpsitr.c create mode 100644 code/mpstr.h create mode 100644 code/trans.c create mode 100644 code/trans.h create mode 100644 code/ztfm.c create mode 100644 design/transform.txt create mode 100644 manual/source/custom/cet/extension.rst create mode 100644 manual/source/custom/cet/hasharray.rst create mode 100644 manual/source/custom/cet/index.rst create mode 100644 manual/source/custom/cet/transform.rst diff --git a/code/comm.gmk b/code/comm.gmk index a2cd68baab..ec8f93bd22 100644 --- a/code/comm.gmk +++ b/code/comm.gmk @@ -221,6 +221,10 @@ MPMCOMMON = \ POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC) MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH) +# Custom CET extensions go here: + +MPM += mpsitr.c trans.c + # These map the source file lists onto object files and dependency files # in the platform/variety directory. @@ -301,6 +305,10 @@ TEST_TARGETS=\ UNBUILDABLE_TARGETS=\ replay # depends on the EPVM pool +# Add tests for the custom CET build here: + +TEST_TARGETS += ztfm + ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS) @@ -568,6 +576,9 @@ $(PFM)/$(VARIETY)/zcoll: $(PFM)/$(VARIETY)/zcoll.o \ $(PFM)/$(VARIETY)/zmess: $(PFM)/$(VARIETY)/zmess.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/ztfm: $(PFM)/$(VARIETY)/ztfm.o \ + $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(PFM)/$(VARIETY)/mpseventcnv: $(PFM)/$(VARIETY)/eventcnv.o \ $(PFM)/$(VARIETY)/mps.a diff --git a/code/commpost.nmk b/code/commpost.nmk index 470db2519b..c669f48996 100644 --- a/code/commpost.nmk +++ b/code/commpost.nmk @@ -219,9 +219,6 @@ $(PFM)\$(VARIETY)\btcv.exe: $(PFM)\$(VARIETY)\btcv.obj \ $(PFM)\$(VARIETY)\bttest.exe: $(PFM)\$(VARIETY)\bttest.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\cvmicv.exe: $(PFM)\$(VARIETY)\cvmicv.obj \ - $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) - $(PFM)\$(VARIETY)\djbench.exe: $(PFM)\$(VARIETY)\djbench.obj \ $(TESTLIBOBJ) $(TESTTHROBJ) diff --git a/code/commpre.nmk b/code/commpre.nmk index 236e08ad2e..3ff41b1766 100644 --- a/code/commpre.nmk +++ b/code/commpre.nmk @@ -113,6 +113,10 @@ OPTIONAL_TARGETS=mpseventsql.exe UNBUILDABLE_TARGETS=replay.exe +# Add tests for the custom CET build here: + +TEST_TARGETS=$(TEST_TARGETS) ztfm.exe + ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS) @@ -189,6 +193,10 @@ TESTTHR = [testthrw3] POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC) MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH) +# Custom CET extensions go here: + +MPMCOMMON = $(MPMCOMMON) [mpsitr] [trans] + # CHECK PARAMETERS # diff --git a/code/mps.c b/code/mps.c index 41c8d076af..5a761c23a7 100644 --- a/code/mps.c +++ b/code/mps.c @@ -81,6 +81,11 @@ #include "vm.c" #include "policy.c" +/* Configura CET customisations */ + +#include "trans.c" +#include "mpsitr.c" + /* Additional pool classes */ #include "poolamc.c" diff --git a/code/mps.xcodeproj/project.pbxproj b/code/mps.xcodeproj/project.pbxproj index 42f60ccb58..be24cfea23 100644 --- a/code/mps.xcodeproj/project.pbxproj +++ b/code/mps.xcodeproj/project.pbxproj @@ -120,6 +120,7 @@ 3114A6B9156E9763001E0AA3 /* PBXTargetDependency */, 31D60063156D3F5C00337B26 /* PBXTargetDependency */, 31D60087156D3FE600337B26 /* PBXTargetDependency */, + 220FD3F419533E8F00967A35 /* PBXTargetDependency */, 3114A6D5156E9839001E0AA3 /* PBXTargetDependency */, 2265D72220E54020003019E8 /* PBXTargetDependency */, 2D07B9791636FCBD00DB751B /* PBXTargetDependency */, @@ -131,6 +132,13 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 220FD3DD195339C000967A35 /* fmtdy.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC6156BE48D00753214 /* fmtdy.c */; }; + 220FD3DE195339C000967A35 /* fmtdytst.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC7156BE48D00753214 /* fmtdytst.c */; }; + 220FD3DF195339C000967A35 /* fmthe.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAE4156BE6D500753214 /* fmthe.c */; }; + 220FD3E1195339C000967A35 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; + 220FD3E3195339C000967A35 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; + 220FD3F119533E7200967A35 /* fmtno.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CACC156BE4C200753214 /* fmtno.c */; }; + 220FD3F219533E7900967A35 /* ztfm.c in Sources */ = {isa = PBXBuildFile; fileRef = 220FD3F019533C3200967A35 /* ztfm.c */; }; 2215A9C9192A495F00E9E2CE /* pooln.c in Sources */ = {isa = PBXBuildFile; fileRef = 22FACEDE18880933000FDBC1 /* pooln.c */; }; 2231BB5118CA97D8002D6322 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; 2231BB5318CA97D8002D6322 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; @@ -349,6 +357,20 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 220FD3DA195339C000967A35 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 31EEABFA156AAF9D00714D05; + remoteInfo = mps; + }; + 220FD3F319533E8F00967A35 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 220FD3D8195339C000967A35; + remoteInfo = ztfm; + }; 2215A9AB192A47BB00E9E2CE /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; @@ -1038,6 +1060,15 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 220FD3E4195339C000967A35 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 2231BB5418CA97D8002D6322 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1493,6 +1524,12 @@ /* Begin PBXFileReference section */ 2213454C1DB0386600E14202 /* prmc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = prmc.h; sourceTree = ""; }; 2213454D1DB038D400E14202 /* prmcxc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = prmcxc.c; sourceTree = ""; }; + 220FD3E9195339C000967A35 /* ztfm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ztfm; sourceTree = BUILT_PRODUCTS_DIR; }; + 220FD3EA195339E500967A35 /* mpsitr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpsitr.c; sourceTree = ""; }; + 220FD3EB195339F000967A35 /* trans.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = trans.c; sourceTree = ""; }; + 220FD3ED19533A8700967A35 /* mpscvm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpscvm.h; sourceTree = ""; }; + 220FD3EE19533A8700967A35 /* trans.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = trans.h; sourceTree = ""; }; + 220FD3F019533C3200967A35 /* ztfm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ztfm.c; sourceTree = ""; }; 2231BB5918CA97D8002D6322 /* locbwcss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = locbwcss; sourceTree = BUILT_PRODUCTS_DIR; }; 2231BB6718CA97DC002D6322 /* locusss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = locusss; sourceTree = BUILT_PRODUCTS_DIR; }; 2231BB6818CA9834002D6322 /* locbwcss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = locbwcss.c; sourceTree = ""; }; @@ -1808,6 +1845,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 220FD3E2195339C000967A35 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 220FD3E3195339C000967A35 /* libmps.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2231BB5218CA97D8002D6322 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -2394,6 +2439,7 @@ 3114A6BA156E9768001E0AA3 /* walkt0.c */, 31D6005E156D3F4A00337B26 /* zcoll.c */, 31D6007B156D3FCC00337B26 /* zmess.c */, + 220FD3F019533C3200967A35 /* ztfm.c */, ); name = Tests; sourceTree = ""; @@ -2485,6 +2531,7 @@ 223E796519EAB00B00DC26A6 /* sncss */, 22EA3F4520D2B0D90065F5B6 /* forktest */, 2265D71D20E53F9C003019E8 /* mpseventpy */, + 220FD3E9195339C000967A35 /* ztfm */, ); name = Products; sourceTree = ""; @@ -2539,7 +2586,9 @@ 311F2F6517398B3B00C15B6A /* mpsacl.h */, 311F2F6617398B3B00C15B6A /* mpsavm.h */, 22FACEDB188808D5000FDBC1 /* mpscmfs.h */, + 220FD3ED19533A8700967A35 /* mpscvm.h */, 31EEABF5156AAF7C00714D05 /* mpsi.c */, + 220FD3EA195339E500967A35 /* mpsitr.c */, 311F2F6717398B3B00C15B6A /* mpsio.h */, 311F2F6817398B3B00C15B6A /* mpslib.h */, 311F2F6917398B3B00C15B6A /* mpstd.h */, @@ -2583,6 +2632,8 @@ 31EEAC1F156AB2B200714D05 /* traceanc.c */, 31EEAC0D156AB27B00714D05 /* tract.c */, 311F2F7A17398B8E00C15B6A /* tract.h */, + 220FD3EB195339F000967A35 /* trans.c */, + 220FD3EE19533A8700967A35 /* trans.h */, 310F5D7118B6675F007EFCBC /* tree.c */, 310F5D7218B6675F007EFCBC /* tree.h */, 31EEAC44156AB32500714D05 /* version.c */, @@ -2667,6 +2718,24 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 220FD3D8195339C000967A35 /* ztfm */ = { + isa = PBXNativeTarget; + buildConfigurationList = 220FD3E5195339C000967A35 /* Build configuration list for PBXNativeTarget "ztfm" */; + buildPhases = ( + 220FD3DB195339C000967A35 /* Sources */, + 220FD3E2195339C000967A35 /* Frameworks */, + 220FD3E4195339C000967A35 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 220FD3D9195339C000967A35 /* PBXTargetDependency */, + ); + name = ztfm; + productName = zmess; + productReference = 220FD3E9195339C000967A35 /* ztfm */; + productType = "com.apple.product-type.tool"; + }; 2231BB4C18CA97D8002D6322 /* locbwcss */ = { isa = PBXNativeTarget; buildConfigurationList = 2231BB5518CA97D8002D6322 /* Build configuration list for PBXNativeTarget "locbwcss" */; @@ -3610,6 +3679,7 @@ 3114A6AB156E9759001E0AA3 /* walkt0 */, 31D60053156D3F3500337B26 /* zcoll */, 31D60070156D3FBC00337B26 /* zmess */, + 220FD3D8195339C000967A35 /* ztfm */, 3114A6C5156E9815001E0AA3 /* mpseventcnv */, 2265D71120E53F9C003019E8 /* mpseventpy */, 2D07B9701636FC9900DB751B /* mpseventsql */, @@ -3695,6 +3765,19 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 220FD3DB195339C000967A35 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 220FD3F219533E7900967A35 /* ztfm.c in Sources */, + 220FD3DD195339C000967A35 /* fmtdy.c in Sources */, + 220FD3DE195339C000967A35 /* fmtdytst.c in Sources */, + 220FD3DF195339C000967A35 /* fmthe.c in Sources */, + 220FD3F119533E7200967A35 /* fmtno.c in Sources */, + 220FD3E1195339C000967A35 /* testlib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2231BB4F18CA97D8002D6322 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4202,6 +4285,16 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 220FD3D9195339C000967A35 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 31EEABFA156AAF9D00714D05 /* mps */; + targetProxy = 220FD3DA195339C000967A35 /* PBXContainerItemProxy */; + }; + 220FD3F419533E8F00967A35 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 220FD3D8195339C000967A35 /* ztfm */; + targetProxy = 220FD3F319533E8F00967A35 /* PBXContainerItemProxy */; + }; 2215A9AA192A47BB00E9E2CE /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 3104AFF1156D37A0000A585A /* all */; @@ -4695,6 +4788,27 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 220FD3E6195339C000967A35 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 220FD3E7195339C000967A35 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 220FD3E8195339C000967A35 /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; 2215A9AE192A47BB00E9E2CE /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -6103,6 +6217,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 220FD3E5195339C000967A35 /* Build configuration list for PBXNativeTarget "ztfm" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 220FD3E6195339C000967A35 /* Debug */, + 220FD3E7195339C000967A35 /* Release */, + 220FD3E8195339C000967A35 /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 2215A9AD192A47BB00E9E2CE /* Build configuration list for PBXAggregateTarget "testci" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/code/mpscvm.h b/code/mpscvm.h new file mode 100644 index 0000000000..886f8e5a3f --- /dev/null +++ b/code/mpscvm.h @@ -0,0 +1,79 @@ +/* mpscvm.h: RAVENBROOK MEMORY POOL SYSTEM C INTERFACE TO CONFIGURA VIRTUAL MACHINE + * + * $Id$ + * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. + * + * This file defines interfaces to miscellaneous extensions to the MPS + * for interfacing to the Configura Virtual Machine. It is not part + * of the open source MPS. + * + * .readership: CVM developers, MPS developers. + */ + +#ifndef mpscvm_h +#define mpscvm_h + +#include "mps.h" + +/* Callbacks indicating that the arena has extended or contracted. + These are used to register chunks with RtlInstallFunctionTableCallback + + so that CVM can unwind the stack through functions in the arena. */ + +typedef void (*mps_arena_extended_t)(mps_arena_t, mps_addr_t, size_t); +typedef void (*mps_arena_contracted_t)(mps_arena_t, mps_addr_t, size_t); + +extern const struct mps_key_s _mps_key_arena_extended; +#define MPS_KEY_ARENA_EXTENDED (&_mps_key_arena_extended) +#define MPS_KEY_ARENA_EXTENDED_FIELD fun + +extern const struct mps_key_s _mps_key_arena_contracted; +#define MPS_KEY_ARENA_CONTRACTED (&_mps_key_arena_contracted) +#define MPS_KEY_ARENA_CONTRACTED_FIELD fun + +extern const struct mps_key_s _mps_key_ap_hash_arrays; +#define MPS_KEY_AP_HASH_ARRAYS (&_mps_key_ap_hash_arrays) +#define MPS_KEY_AP_HASH_ARRAYS_FIELD b + +#endif /* mpscvm_h */ + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2011 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/mpsitr.c b/code/mpsitr.c new file mode 100644 index 0000000000..866731fd3d --- /dev/null +++ b/code/mpsitr.c @@ -0,0 +1,135 @@ +/* mpsitr.c: MEMORY POOL SYSTEM C INTERFACE LAYER TO TRANSFORMS + * + * $Id$ + * Copyright 2011 Ravenbrook Limited. See end of file for license. + * + * .purpose: This code bridges between the MPS interface to C to Transforms + * and the internal implementation of Transforms. It is + * analogous to the MPS C Interface Layer , but for the Transforms + * extension. + */ + +#include "mpm.h" +#include "mpstr.h" +#include "trans.h" +#include "ss.h" + + +SRCID(mpsitr, "$Id$"); + + +mps_res_t mps_transform_create(mps_transform_t *mps_transform_o, + mps_arena_t arena) +{ + Transform transform = NULL; + Res res; + + AVER(mps_transform_o != NULL); + + ArenaEnter(arena); + res = TransformCreate(&transform, arena); + ArenaLeave(arena); + if (res != ResOK) + return res; + + *mps_transform_o = (mps_transform_t)transform; + return MPS_RES_OK; +} + + +mps_res_t mps_transform_add_oldnew(mps_transform_t transform, + mps_addr_t *mps_old_list, + mps_addr_t *mps_new_list, + size_t mps_count) +{ + Ref *old_list = (Ref *)mps_old_list; + Ref *new_list = (Ref *)mps_new_list; + Count count = mps_count; + Arena arena; + Res res; + + AVER(mps_old_list != NULL); + AVER(mps_new_list != NULL); + /* count: cannot check */ + + arena = TransformArena(transform); + + ArenaEnter(arena); + res = TransformAddOldNew(transform, old_list, new_list, count); + ArenaLeave(arena); + + return res; +} + + +mps_res_t mps_transform_apply(mps_bool_t *applied_o, + mps_transform_t transform) +{ + Arena arena; + Res res; + + AVER(applied_o != NULL); + + arena = TransformArena(transform); + ArenaEnter(arena); + STACK_CONTEXT_BEGIN(arena) { + res = TransformApply(applied_o, transform); + } STACK_CONTEXT_END(arena); + ArenaLeave(arena); + + return res; +} + + +void mps_transform_destroy(mps_transform_t transform) +{ + Arena arena; + + arena = TransformArena(transform); + + ArenaEnter(arena); + TransformDestroy(transform); + ArenaLeave(arena); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2011 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/mpstr.h b/code/mpstr.h new file mode 100644 index 0000000000..d718e4ed1e --- /dev/null +++ b/code/mpstr.h @@ -0,0 +1,66 @@ +/* mpstr.h: RAVENBROOK MEMORY POOL SYSTEM C INTERFACE TO TRANSFORMS + * + * $Id$ + * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * + * .readership: customers, MPS developers. + */ + +#ifndef mpstr_h +#define mpstr_h + +#include "mps.h" + +typedef struct mps_transform_s *mps_transform_t; + +extern mps_res_t mps_transform_create(mps_transform_t *, mps_arena_t); + +extern mps_res_t mps_transform_add_oldnew(mps_transform_t, mps_addr_t *, mps_addr_t *, size_t); + +extern mps_res_t mps_transform_apply(mps_bool_t *, mps_transform_t); + +extern void mps_transform_destroy(mps_transform_t); + +#endif /* mpstr_h */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2011 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/protw3.c b/code/protw3.c index 6c57731ae4..956bc8293b 100644 --- a/code/protw3.c +++ b/code/protw3.c @@ -113,15 +113,16 @@ LONG WINAPI ProtSEHfilter(LPEXCEPTION_POINTERS info) /* ProtSetup -- set up the protection system */ +static void *vehHandle; + void ProtSetup(void) { - void *handler; /* See "AddVectoredExceptionHandler function (Windows)" */ /* ProtSetup is called only once per process, not once per arena, so this exception handler is only installed once. */ - handler = AddVectoredExceptionHandler(1uL, ProtSEHfilter); - AVER(handler != NULL); + vehHandle = AddVectoredExceptionHandler(1uL, ProtSEHfilter); + AVER(vehHandle != NULL); } diff --git a/code/testlib.h b/code/testlib.h index f140b0a9e1..9a414f2b5d 100644 --- a/code/testlib.h +++ b/code/testlib.h @@ -114,6 +114,7 @@ #if defined(MPS_OS_W3) && defined(MPS_ARCH_I6) #define PRIuLONGEST "llu" +#define PRIdLONGEST "lld" #define SCNuLONGEST "llu" #define SCNXLONGEST "llX" #define PRIXLONGEST "llX" @@ -122,6 +123,7 @@ typedef long long longest_t; #define MPS_WORD_CONST(n) (n##ull) #else #define PRIuLONGEST "lu" +#define PRIdLONGEST "ld" #define SCNuLONGEST "lu" #define SCNXLONGEST "lX" #define PRIXLONGEST "lX" diff --git a/code/trans.c b/code/trans.c new file mode 100644 index 0000000000..565b15800d --- /dev/null +++ b/code/trans.c @@ -0,0 +1,397 @@ +/* trans.c: RAVENBROOK MEMORY POOL SYSTEM TRANSFORMS + * + * $Id$ + * Copyright 2011-2018 Ravenbrook Limited. See end of file for license. + * + * This code is specific to Configura. + * + * A transform is a special kind of garbage collection that replaces references + * to a set of objects. The transform is piggybacked onto a garbage + * collection by overriding the fix method for a trace. The mapping used to + * replace the references is built up in a hash table by the client. + * + * + * Rationale + * + * This design was arrived at after some pain. The MPS isn't really designed + * for this kind of thing, and the pools generally assume that they're doing + * a garbage collection when they're asked to condemn, scan, fix, and reclaim + * stuff. This makes it very hard to apply the transform without also doing + * a garbage collection. Changing this would require a significant reworking + * of the MPS to generalise its ideas, and would bloat the pool classes. + * + * + * Assumptions: + * + * - Single-threaded mutator. In fact this code might work if other mutator + * threads are running, since the shield ought to operate correctly. + * However, in this case there can only be one trace running so that the + * correct fix method is chosen by ScanStateInit. + */ + +#include "trans.h" +#include "table.h" + + +#define TransformSig ((Sig)0x51926A45) /* SIGnature TRANSform */ + +typedef struct mps_transform_s { + Sig sig; /* */ + Arena arena; /* owning arena */ + Table oldToNew; /* map to apply to refs */ + Epoch epoch; /* epoch in which transform was created */ + Bool aborted; /* no longer transforming, just GCing */ +} TransformStruct; + + +Bool TransformCheck(Transform transform) +{ + CHECKS(Transform, transform); + CHECKU(Arena, transform->arena); + /* .check.boot: avoid bootstrap problem in transformTableAlloc where + transformTableFree checks the transform while the table is being + destroyed */ + if (transform->oldToNew != NULL) + CHECKD(Table, transform->oldToNew); + CHECKL(BoolCheck(transform->aborted)); + CHECKL(transform->epoch <= ArenaEpoch(transform->arena)); + return TRUE; +} + + +/* Allocator functions for the Table oldToNew */ + +static void *transformTableAlloc(void *closure, size_t size) +{ + Transform transform = (Transform)closure; + Res res; + void *p; + + AVERT(Transform, transform); + + res = ControlAlloc(&p, transform->arena, size); + if (res != ResOK) + return NULL; + + return p; +} + +static void transformTableFree(void *closure, void *p, size_t size) +{ + Transform transform = (Transform)closure; + AVERT(Transform, transform); + ControlFree(transform->arena, p, size); +} + + +Res TransformCreate(Transform *transformReturn, Arena arena) +{ + Transform transform; + Res res; + void *p; + + AVER(transformReturn != NULL); + AVERT(Arena, arena); + + res = ControlAlloc(&p, arena, sizeof(TransformStruct)); + if (res != ResOK) + goto failAlloc; + transform = (Transform)p; + + transform->oldToNew = NULL; + transform->arena = arena; + transform->epoch = ArenaEpoch(arena); + transform->aborted = FALSE; + + transform->sig = TransformSig; + + AVERT(Transform, transform); + + res = TableCreate(&transform->oldToNew, + 0, /* don't grow table until TransformAddOldNew */ + transformTableAlloc, + transformTableFree, + transform, + 0, 1); /* these can't be old references */ + if (res != ResOK) + goto failTable; + + *transformReturn = transform; + return ResOK; + +failTable: + ControlFree(arena, transform, sizeof(TransformStruct)); +failAlloc: + return res; +} + + +void TransformDestroy(Transform transform) +{ + Arena arena; + Table oldToNew; + + AVERT(Transform, transform); + + /* TODO: Log some transform statistics. */ + + /* Workaround bootstrap problem, see .check.boot */ + oldToNew = transform->oldToNew; + transform->oldToNew = NULL; + TableDestroy(oldToNew); + + arena = TransformArena(transform); + transform->sig = SigInvalid; + ControlFree(arena, transform, sizeof(TransformStruct)); +} + + +/* TransformArena -- return transform's arena + * + * Must be thread-safe as it is called outside the arena lock. See + * + */ + +Arena TransformArena(Transform transform) +{ + Arena arena; + AVER(TESTT(Transform, transform)); + arena = transform->arena; + AVER(TESTT(Arena, arena)); + return arena; +} + + +Res TransformAddOldNew(Transform transform, + Ref old_list[], + Ref new_list[], + Count count) +{ + Res res; + Index i; + Count added = 0; + + AVERT(Transform, transform); + AVER(old_list != NULL); + AVER(new_list != NULL); + /* count: cannot check */ + + res = TableGrow(transform->oldToNew, count); + if (res != ResOK) + return res; + + for(i = 0; i < count; ++i) { + /* NOTE: If the mutator isn't adding references while the arena is parked, + we might need to access the client-provided lists, using ArenaRead. */ + if(old_list[i] == NULL) + continue; /* permitted, but no transform to do */ + if(old_list[i] == new_list[i]) + continue; /* ignore identity-transforms */ + + /* Old refs must be in managed memory. */ + { + Seg seg; + AVER(SegOfAddr(&seg, transform->arena, old_list[i])); + } + + res = TableDefine(transform->oldToNew, (Word)old_list[i], new_list[i]); + AVER(res != ResFAIL); /* It's a static error to add the same old twice. */ + if (res != ResOK) + return res; + + ++added; + } + + AVERT(Transform, transform); + + return ResOK; +} + + +/* TransformApply -- transform references on the heap */ + +static Res transformFix(Seg seg, ScanState ss, Ref *refIO) +{ + Ref ref; + Transform transform; + Res res; + + AVERT_CRITICAL(Seg, seg); + AVERT_CRITICAL(ScanState, ss); + AVER_CRITICAL(refIO != NULL); + + transform = ss->fixClosure; + AVERT_CRITICAL(Transform, transform); + + if (!transform->aborted) { + void *refNew; + + ref = *refIO; + + if (TableLookup(&refNew, transform->oldToNew, (Word)ref)) { + if (ss->rank == RankAMBIG) { + /* We rely on the fact that all ambiguous references are fixed before + any others, so no references will have transformed by the time we + abort. + NOTE: Configura CVM prints a message and exits if a transform + fails. See Configura's + //depot/project/cet/kernel/internal/version3.2/cvm/c2/updateHeap2.m#4 + line 133.*/ + transform->aborted = TRUE; + } else { + /* NOTE: We could fix refNew in the table before copying it, + since any summaries etc. collected in the scan state will still + apply when it's copied. That could save a few snap-outs. */ + *refIO = refNew; + } + } + } + + /* Now progress to a normal GC fix. */ + /* TODO: Make a clean interface to this kind of dynamic binding. */ + ss->fix = ss->arena->emergency ? SegFixEmergency : SegFix; + TRACE_SCAN_BEGIN(ss) { + res = TRACE_FIX12(ss, refIO); + } TRACE_SCAN_END(ss); + ss->fix = transformFix; + + return res; +} + + +static void transformCondemn(void *closure, Word old, void *value) +{ + Seg seg; + GenDesc gen; + Bool b; + Trace trace = closure; + + AVERT(Trace, trace); + UNUSED(value); + + /* Find segment containing old address. */ + b = SegOfAddr(&seg, trace->arena, (Ref)old); + AVER(b); /* old refs must be in managed memory, else client param error */ + + /* Condemn generation containing seg if not already condemned. */ + gen = PoolSegPoolGen(SegPool(seg), seg)->gen; + AVERT(GenDesc, gen); + if (RingIsSingle(&gen->trace[trace->ti].traceRing)) + GenDescStartTrace(gen, trace); +} + + +Res TransformApply(Bool *appliedReturn, Transform transform) +{ + Res res; + Arena arena; + Globals globals; + Trace trace; + double mortality; + + AVER(appliedReturn != NULL); + AVERT(Transform, transform); + + arena = TransformArena(transform); + + /* If there have been any flips since the transform was created, the old + and new pointers will be invalid, since they are not scanned as roots. + NOTE: Configura CVM parks the arena before adding references. See + //depot/project/cet/kernel/internal/version3.2/cvm/c2/updateHeap2.m#4 + line 114. */ + if (transform->epoch != ArenaEpoch(arena)) + return ResPARAM; + + globals = ArenaGlobals(arena); + AVERT(Globals, globals); + + ArenaPark(globals); + + res = TraceCreate(&trace, arena, TraceStartWhyEXTENSION); + AVER(res == ResOK); /* parking should make a trace available */ + if (res != ResOK) + return res; + + /* Condemn the generations containing the transform's old objects, + so that all references to them are scanned. */ + TraceCondemnStart(trace); + TableMap(transform->oldToNew, transformCondemn, trace); + res = TraceCondemnEnd(&mortality, trace); + if (res != ResOK) { + /* Nothing to transform. */ + TraceDestroyInit(trace); + goto done; + } + + trace->fix = transformFix; + trace->fixClosure = transform; + + res = TraceStart(trace, 1.0, 0.0); + AVER(res == ResOK); /* transformFix can't fail */ + + /* If transformFix during traceFlip found ambiguous references and + aborted the transform then the rest of the trace is just a normal GC. + Note that aborting a trace part-way through is pretty much impossible + without corrupting the mutator graph. We could safely + if (transform->aborted) { + trace->fix = PoolFix; + trace->fixClosure = NULL; + } + */ + + /* Force the trace to complete now. */ + ArenaPark(globals); + +done: + if (transform->aborted) { + *appliedReturn = FALSE; + } else { + *appliedReturn = TRUE; + /* I'm not sure why the interface is defined this way. RB 2012-08-03 */ + TransformDestroy(transform); + } + + return ResOK; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2011-2018 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/trans.h b/code/trans.h new file mode 100644 index 0000000000..4a19f5fe92 --- /dev/null +++ b/code/trans.h @@ -0,0 +1,75 @@ +/* trans.h: RAVENBROOK MEMORY POOL SYSTEM TRANSFORMS + * + * $Id$ + * Copyright 2011 Ravenbrook Limited. See end of file for license. + */ + +#ifndef trans_h +#define trans_h + +#include "mpm.h" + + +typedef struct mps_transform_s *Transform; + +typedef struct OldNewStruct *OldNew; + +extern Res TransformCreate(Transform *transformReturn, Arena arena); + +extern Res TransformAddOldNew(Transform transform, + Ref old_list[], + Ref new_list[], + Count count); + +extern Res TransformApply(Bool *appliedReturn, Transform transform); + +extern void TransformDestroy(Transform transform); + +extern Bool TransformCheck(Transform transform); + +extern Arena TransformArena(Transform transform); + + +#endif /* trans_h */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2011 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/ztfm.c b/code/ztfm.c new file mode 100644 index 0000000000..8dc81a5be2 --- /dev/null +++ b/code/ztfm.c @@ -0,0 +1,1465 @@ +/* ztfm.c: Transform test + * + * $Id$ + * Copyright (c) 2010 Ravenbrook Limited. See end of file for license. + * Portions copyright (C) 2002 Global Graphics Software. + * + * OBJECTIVE + * + * + * DESIGN OVERVIEW + * + * CODE OVERVIEW + * + * DEPENDENCIES + * + * This test uses the dylan object format, but the reliance on this + * particular format is not great and could be removed. + * + * + * BUGS, FUTURE IMPROVEMENTS, ETC + * + * HISTORY + * + * This code was created by first copying . + */ + +#include "fmtdy.h" +#include "fmtdytst.h" +#include "mps.h" +#include "mpsavm.h" +#include "mpscamc.h" +#include "mpslib.h" +#include "mpstr.h" +#include "testlib.h" + +#include /* printf */ + + +#define progressf(args) \ + printf args; +#define Xprogressf(args) \ + do{if(0)printf args;}while(FALSE) + +/* testChain -- generation parameters for the test */ +#define genCOUNT 2 +static mps_gen_param_s testChain[genCOUNT] = { + { 100, 0.85 }, { 170, 0.45 } }; + + +/* myroot -- arrays of references that are the root */ +#define myrootAmbigCOUNT 30000 +static void *myrootAmbig[myrootAmbigCOUNT]; +#define myrootExactCOUNT 1000000 +static void *myrootExact[myrootExactCOUNT]; + +static mps_root_t root_stackreg; +static void *stack_start; +static mps_thr_t stack_thr; + + +/* =========== Transform ==============*/ +static void get(mps_arena_t arena); + +static ulongest_t serial = 0; + +/* Tree nodes + * + * To make a node: + * - use next unique serial; + * - choose an id (eg. next in sequence); + * - choose a version (eg. 0). + * + * To make a New node from (or in connection with) an Old: + * - use next unique serial; + * - copy id of Old; + * - ver = ver(Old) + 1. + * + * To invalidate an Old node, when adding an OldNew pair for it: + * - ver = -1 + */ + +struct node_t { + mps_word_t word0; + mps_word_t word1; + mps_word_t serial_dyi; /* unique across every node ever */ + mps_word_t id_dyi; /* .id: replacement nodes copy this */ + mps_word_t ver_dyi; /* .version: distinguish new from old */ + struct node_t *left; + struct node_t *right; + mps_word_t tour_dyi; /* latest tour that visited this node */ + mps_word_t tourIdHash_dyi; /* hash of node ids, computed last tour */ +}; + +/* Tour -- a particular journey to visit to every node in the world + * + * A tour starts with the node at world[0], tours the graph reachable + * from it, then any further bits of graph reachable from world[1], + * and so on. + * + * As it does so, the tour computes a tourReport, characterising the + * state of everything reachable (from world) in the form a few numbers. + * + * Each tour explores the subgraph rooted at each node exactly once. + * That is: if the tour re-encounters a node already visited on that + * tour, it uses the values already computed for that node. + * + * The tourIdHash deliberately depends on the order in which nodes are + * encountered. Therefore, the tourIdHash of a tour depends on the + * entirety of the state of the world and all the world-reachable nodes. + * + * The tourVerSum, being a simple sum, does not depend on the order of + * visiting. + */ + +enum { + cVer = 10 +}; +typedef struct tourReportStruct { + ulongest_t tour; /* tour serial */ + ulongest_t tourIdHash; /* hash of node ids, computed last tour */ + ulongest_t acNodesVer[cVer]; /* count of nodes of each Ver */ +} *tourReport; + +static void tour_subgraph(tourReport tr_o, struct node_t *node); + +static ulongest_t tourSerial = 0; + +static void tourWorld(tourReport tr_o, mps_addr_t *world, ulongest_t countWorld) +{ + ulongest_t i; + + tourSerial += 1; + tr_o->tour = tourSerial; + tr_o->tourIdHash = 0; + for(i = 0; i < cVer; i++) { + tr_o->acNodesVer[i] = 0; + } + Xprogressf(( "[tour %"SCNuLONGEST"] BEGIN, world: %p, countWorld: %"SCNuLONGEST"\n", + tourSerial, (void *)world, countWorld)); + + for(i = 0; i < countWorld; i++) { + struct node_t *node; + node = (struct node_t *)world[i]; + tour_subgraph(tr_o, node); + tr_o->tourIdHash += i * (node ? DYI_INT(node->tourIdHash_dyi) : 0); + } +} + +static void tour_subgraph(tourReport tr_o, struct node_t *node) +{ + ulongest_t tour; + ulongest_t ver; + ulongest_t id; + struct node_t *left; + struct node_t *right; + ulongest_t tourIdHashLeft; + ulongest_t tourIdHashRight; + ulongest_t tourIdHash; + + Insist(tr_o != NULL); + + /* node == NULL is permitted */ + if(node == NULL) + return; + + tour = tr_o->tour; + if(DYI_INT(node->tour_dyi) == tour) + return; /* already visited */ + + /* this is a newly discovered node */ + Insist(DYI_INT(node->tour_dyi) < tour); + + /* mark as visited */ + node->tour_dyi = INT_DYI(tour); + + /* 'local' idHash = id, used for any re-encounters while computing the computed idHash */ + node->tourIdHash_dyi = node->id_dyi; + + /* record this node in the array of ver counts */ + ver = DYI_INT(node->ver_dyi); + Insist(ver < cVer); + tr_o->acNodesVer[ver] += 1; + + /* tour the subgraphs (NULL is permitted) */ + left = node->left; + right = node->right; + tour_subgraph(tr_o, left); + tour_subgraph(tr_o, right); + + /* computed idHash of subgraph at this node */ + id = DYI_INT(node->id_dyi); + tourIdHashLeft = left ? DYI_INT(left->tourIdHash_dyi) : 0; + tourIdHashRight = right ? DYI_INT(right->tourIdHash_dyi) : 0; + tourIdHash = (13*id + 17*tourIdHashLeft + 19*tourIdHashRight) & DYLAN_UINT_MASK; + Insist(tourIdHash <= DYLAN_UINT_MAX); + node->tourIdHash_dyi = INT_DYI(tourIdHash); + + Insist(DYI_INT(node->tour_dyi) == tour); + Xprogressf(( "[tour %"SCNuLONGEST"] new completed node: %p, ver: %"SCNuLONGEST", tourIdHash: %"SCNuLONGEST"\n", + tour, (void*)node, ver, tourIdHash )); +} + +static struct tourReportStruct trBefore; +static struct tourReportStruct trAfter; + +static void before(mps_addr_t *world, ulongest_t countWorld) +{ + tourWorld(&trBefore, world, countWorld); +} + +static void after(mps_addr_t *world, ulongest_t countWorld, + ulongest_t verOld, + longest_t deltaCVerOld, + ulongest_t verNew, + longest_t deltaCVerNew) +{ + longest_t dCVerOld; + longest_t dCVerNew; + + tourWorld(&trAfter, world, countWorld); + + dCVerOld = ((long)trAfter.acNodesVer[verOld] - (long)trBefore.acNodesVer[verOld]); + dCVerNew = ((long)trAfter.acNodesVer[verNew] - (long)trBefore.acNodesVer[verNew]); + + progressf(("tourWorld: (%"PRIuLONGEST" %"PRIuLONGEST":%"PRIuLONGEST"/%"PRIuLONGEST":%"PRIuLONGEST") -> (%"PRIuLONGEST" %"PRIuLONGEST":%+"PRIdLONGEST"/%"PRIuLONGEST":%+"PRIdLONGEST"), %s\n", + trBefore.tourIdHash, + verOld, + trBefore.acNodesVer[verOld], + verNew, + trBefore.acNodesVer[verNew], + + trAfter.tourIdHash, + verOld, + dCVerOld, + verNew, + dCVerNew, + + trBefore.tourIdHash == trAfter.tourIdHash ? "same" : "XXXXX DIFFERENT XXXXX" + )); + Insist(trBefore.tourIdHash == trAfter.tourIdHash); + Insist(dCVerOld == deltaCVerOld); + Insist(dCVerNew == deltaCVerNew); +} + + +static mps_res_t mps_arena_transform_objects_list(mps_bool_t *transform_done_o, + mps_arena_t mps_arena, + mps_addr_t *old_list, + size_t old_list_count, + mps_addr_t *new_list, + size_t new_list_count) +{ + mps_res_t res; + mps_transform_t transform; + mps_bool_t applied = FALSE; + + Insist(old_list_count == new_list_count); + + res = mps_transform_create(&transform, mps_arena); + if(res == MPS_RES_OK) { + /* We have a transform */ + res = mps_transform_add_oldnew(transform, old_list, new_list, old_list_count); + if(res == MPS_RES_OK) { + res = mps_transform_apply(&applied, transform); + } + if(applied) { + /* Transform has been destroyed */ + Insist(res == MPS_RES_OK); + } else { + mps_transform_destroy(transform); + } + } + + /* Always set *transform_done_o (even if there is also a non-ResOK */ + /* return code): it is a status report, not a material return. */ + *transform_done_o = applied; + return res; +} + +static void Transform(mps_arena_t arena, mps_ap_t ap) +{ + ulongest_t i; + ulongest_t keepCount = 0; + mps_word_t v; + struct node_t *node; + mps_res_t res; + mps_bool_t transform_done; + ulongest_t old, new; + ulongest_t perset; + + mps_arena_park(arena); + + { + /* Test with sets of pre-built nodes, a known distance apart. + * + * This gives control over whether new nodes are on the same + * segment as the olds or not. + */ + + ulongest_t iPerset; + ulongest_t aPerset[] = {0, 1, 1, 10, 10, 1000, 1000}; + ulongest_t cPerset = NELEMS(aPerset); + ulongest_t stepPerset; + ulongest_t countWorld = 0; + + /* randomize the order of set sizes from aPerset */ + stepPerset = 1 + (rnd() % (cPerset - 1)); + + progressf(("INT_DYI(1): %"PRIuLONGEST"; DYI_INT(5): %"PRIuLONGEST"\n", + (ulongest_t)INT_DYI(1), (ulongest_t)DYI_INT(5) )); + progressf(("Will make and transform sets of old nodes into new nodes. Set sizes: ")); + for(iPerset = stepPerset; + aPerset[iPerset] != 0; + iPerset = (iPerset + stepPerset) % cPerset) { + countWorld += aPerset[iPerset] * 2; /* 2: old + new */ + progressf(("%"PRIuLONGEST", ", aPerset[iPerset])); + } + progressf(("total: %"PRIuLONGEST".\n", countWorld)); + Insist(countWorld <= myrootExactCOUNT); + + keepCount = 0; + + for(iPerset = stepPerset; + aPerset[iPerset] != 0; + iPerset = (iPerset + stepPerset) % cPerset) { + ulongest_t j; + ulongest_t first; + ulongest_t skip; + ulongest_t count; + + perset = aPerset[iPerset]; + first = keepCount; + skip = 0; + count = perset; + progressf(("perset: %"PRIuLONGEST", first: %"PRIuLONGEST"\n", perset, first)); + + /* Make a set of olds, and a set of news */ + for(j = 0; j < 2 * perset; j++) { + ulongest_t slots = (sizeof(struct node_t) / sizeof(mps_word_t)) - 2; + /* make_dylan_vector: fills slots with INT_DYI(0) */ + die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); + node = (struct node_t *)v; + node->serial_dyi = INT_DYI(serial++); + node->id_dyi = INT_DYI(j % perset); + node->ver_dyi = INT_DYI(1 + (j >= perset)); + node->left = NULL; + node->right = NULL; + node->tour_dyi = INT_DYI(tourSerial); + node->tourIdHash_dyi = INT_DYI(0); + myrootExact[keepCount++ % myrootExactCOUNT] = (void*)v; + get(arena); + /*printf("Object %"PRIuLONGEST" at %p.\n", keepCount, (void*)v);*/ + } + v = 0; + + /* >=10? pick subset */ + if(perset >= 10) { + /* subset of [first..first+perset) */ + + skip = (rnd() % (2 * perset)); + if(skip > (perset - 1)) + skip = 0; + + count = 1 + rnd() % (2 * (perset - skip)); + if(skip + count > perset) + count = perset - skip; + + Insist(skip < perset); + Insist(count >= 1); + Insist(skip + count <= perset); + } + + /* >=10? sometimes build tree */ + if(perset >= 10 && count >= 4 && rnd() % 2 == 0) { + struct node_t **oldNodes = (struct node_t **)&myrootExact[first + skip]; + struct node_t **newNodes = (struct node_t **)&myrootExact[first + skip + perset]; + progressf(("Building tree in %"PRIuLONGEST" nodes.\n", count)); + for(j = 1; (2 * j) + 1 < count; j++) { + oldNodes[j]->left = oldNodes[2 * j]; + oldNodes[j]->right = oldNodes[(2 * j) + 1]; + if(1){newNodes[j]->left = newNodes[2 * j]; + newNodes[j]->right = newNodes[(2 * j) + 1];} + } + } + + /* transform {count} olds into {count} news */ + before(myrootExact, countWorld); + /* after(myrootExact, countWorld, 1, 0, 2, 0); */ + progressf(("Transform [%"PRIuLONGEST"..%"PRIuLONGEST") to [%"PRIuLONGEST"..%"PRIuLONGEST").\n", + first + skip, first + skip + count, first + skip + perset, first + skip + count + perset)); + res = mps_arena_transform_objects_list(&transform_done, arena, + &myrootExact[first + skip], count, + &myrootExact[first + skip + perset], count); + Insist(res == MPS_RES_OK); + Insist(transform_done); + /* Olds decrease; news were in world already so don't increase. */ + after(myrootExact, countWorld, 1, -(longest_t)count, 2, 0); + } + } + + { + /* Transforming in various situations + * + * First, make two sets of 1024 nodes. + */ + + perset = 1024; + Insist(2*perset < myrootExactCOUNT); + for(keepCount = 0; keepCount < 2*perset; keepCount++) { + ulongest_t slots = (sizeof(struct node_t) / sizeof(mps_word_t)) - 2; + /* make_dylan_vector: fills slots with INT_DYI(0) */ + die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); + node = (struct node_t *)v; + node->serial_dyi = INT_DYI(serial++); + node->id_dyi = INT_DYI(keepCount % perset); + node->ver_dyi = INT_DYI(1 + (keepCount >= perset)); + node->left = NULL; + node->right = NULL; + myrootExact[keepCount % myrootExactCOUNT] = (void*)v; + get(arena); + /* printf("Object %u at %p.\n", keepCount, (void*)v); */ + } + v = 0; + + + /* Functions before() and after() checksum the world, and verify + * that the expected transform occurred. + */ + before(myrootExact, perset); + after(myrootExact, perset, 1, 0, 2, 0); + + /* Don't transform node 0: its ref coincides with a segbase, so + * there are probably ambiguous refs to it on the stack. + * Don't transform last node either: this test code may leave an + * ambiguous reference to it on the stack. + */ + + /* Refs in root */ + /* ============ */ + old = 1; + new = 1 + perset; + Insist(myrootExact[old] != myrootExact[new]); + before(myrootExact, perset); + res = mps_arena_transform_objects_list(&transform_done, arena, &myrootExact[old], 1, &myrootExact[new], 1); + Insist(res == MPS_RES_OK); + Insist(transform_done); + Insist(myrootExact[old] == myrootExact[new]); + after(myrootExact, perset, 1, -1, 2, +1); + + /* Refs in root: ambiguous ref causes failure */ + /* ========================================== */ + old = 2; + new = 2 + perset; + Insist(myrootExact[old] != myrootExact[new]); + /* Make an ambiguous reference. This must make the transform fail. */ + myrootAmbig[1] = myrootExact[old]; + before(myrootExact, perset); + res = mps_arena_transform_objects_list(&transform_done, arena, &myrootExact[old], 1, &myrootExact[new], 1); + Insist(res == MPS_RES_OK); + Insist(!transform_done); + Insist(myrootExact[old] != myrootExact[new]); + after(myrootExact, perset, 1, 0, 2, 0); + + /* Ref in an object */ + /* ================ */ + old = 3; + new = 3 + perset; + node = myrootExact[4]; + progressf(("node: %p\n", (void *)node)); + node->left = myrootExact[old]; + Insist(myrootExact[old] != myrootExact[new]); + before(myrootExact, perset); + res = mps_arena_transform_objects_list(&transform_done, arena, &myrootExact[old], 1, &myrootExact[new], 1); + Insist(res == MPS_RES_OK); + Insist(transform_done); + Insist(myrootExact[old] == myrootExact[new]); + after(myrootExact, perset, 1, -1, 2, +1); + } + + { + /* Tests with mps_transform_t + * + * **** USES OBJECTS CREATED IN PREVIOUS TEST GROUP **** + */ + + mps_transform_t t1; + mps_transform_t t2; + mps_bool_t applied = FALSE; + ulongest_t k, l; + mps_addr_t nullref1 = NULL; + mps_addr_t nullref2 = NULL; + + k = 9; /* start with this object (in set of 1024) */ + + /* Destroy */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + mps_transform_destroy(t1); + t1 = NULL; + + /* Empty (no add) */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_OK); + Insist(applied); + t1 = NULL; + after(myrootExact, perset, 1, 0, 2, 0); + + /* Identity-transform */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + for(l = k + 4; k < l; k++) { + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k], 1); + } + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k], 10); + k += 10; + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_OK); + Insist(applied); + t1 = NULL; + after(myrootExact, perset, 1, 0, 2, 0); + + /* Mixed non-trivial, NULL- and identity-transforms */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + { + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 1); + k += 1; + /* NULL */ + mps_transform_add_oldnew(t1, &nullref1, &myrootExact[k + perset], 1); + k += 1; + /* identity */ + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k], 1); + k += 1; + /* NULL */ + mps_transform_add_oldnew(t1, &nullref2, &myrootExact[k + perset], 1); + k += 1; + } + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 10); + k += 10; + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_OK); + Insist(applied); + t1 = NULL; + after(myrootExact, perset, 1, -11, 2, +11); + + /* Non-trivial transform */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + for(l = k + 4; k < l; k++) { + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 1); + } + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 10); + k += 10; + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_OK); + Insist(applied); + t1 = NULL; + after(myrootExact, perset, 1, -14, 2, +14); + + /* Two transforms, first destroyed unused */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 10); + k += 10; + l = k; + res = mps_transform_create(&t2, arena); + Insist(res == MPS_RES_OK); + mps_transform_add_oldnew(t2, &myrootExact[l], &myrootExact[l + perset], 10); + l += 10; + res = mps_transform_apply(&applied, t2); + Insist(res == MPS_RES_OK); + Insist(applied); + t2 = NULL; + mps_transform_destroy(t1); + after(myrootExact, perset, 1, -10, 2, +10); + + /* Two transforms, both live [-- not supported yet. RHSK 2010-12-16] */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 10); + k += 10; + l = k; + res = mps_transform_create(&t2, arena); + Insist(res == MPS_RES_OK); + mps_transform_add_oldnew(t2, &myrootExact[l], &myrootExact[l + perset], 10); + l += 10; + res = mps_transform_apply(&applied, t2); + Insist(res == MPS_RES_OK); + Insist(applied); + t2 = NULL; + k = l; + after(myrootExact, perset, 1, -10, 2, +10); + + /* Attempt to destroy after applied. */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_OK); + Insist(applied); + after(myrootExact, perset, 1, 0, 2, 0); + } + + /* Large number of objects */ + { + ulongest_t count; + mps_transform_t t; + mps_bool_t applied; + + /* LARGE! */ + perset = myrootExactCOUNT / 2; + + Insist(2*perset <= myrootExactCOUNT); + for(keepCount = 0; keepCount < 2*perset; keepCount++) { + ulongest_t slots = (sizeof(struct node_t) / sizeof(mps_word_t)) - 2; + /* make_dylan_vector: fills slots with INT_DYI(0) */ + die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); + node = (struct node_t *)v; + node->serial_dyi = INT_DYI(serial++); + node->id_dyi = INT_DYI(keepCount % perset); + node->ver_dyi = INT_DYI(1 + (keepCount >= perset)); + node->left = NULL; + node->right = NULL; + myrootExact[keepCount % myrootExactCOUNT] = (void*)v; + get(arena); + /* printf("Object %u at %p.\n", keepCount, (void*)v); */ + } + v = 0; + + /* Refs in root */ + /* ============ */ + /* don't transform 0: its ref coincides with a segbase, so causes ambig refs on stack */ + /* don't transform last: its ambig ref may be left on the stack */ + old = 1; + new = 1 + perset; + count = perset - 2; + Insist(myrootExact[old] != myrootExact[new]); + before(myrootExact, perset); + res = mps_transform_create(&t, arena); + Insist(res == MPS_RES_OK); + for(i = 0; i < count; i++) { + res = mps_transform_add_oldnew(t, &myrootExact[old + i], &myrootExact[new + i], 1); + Insist(res == MPS_RES_OK); + } + res = mps_transform_apply(&applied, t); + Insist(applied); + Insist(myrootExact[old] == myrootExact[new]); + after(myrootExact, perset, 1, -(longest_t)count, 2, +(longest_t)count); + } + + printf(" ...made and kept: %"PRIuLONGEST" objects.\n", + keepCount); +} + + +static ulongest_t cols(size_t bytes) +{ + double M; /* Mebibytes */ + ulongest_t cM; /* hundredths of a Mebibyte */ + + M = (double)bytes / (1UL<<20); + cM = (ulongest_t)(M * 100 + 0.5); /* round to nearest */ + return cM; +} + +/* showStatsAscii -- present collection stats, 'graphically' + * + */ +static void showStatsAscii(size_t notcon, size_t con, size_t live, size_t alimit) +{ + ulongest_t n = cols(notcon); + ulongest_t c = cols(notcon + con); + ulongest_t l = cols(notcon + live); /* a fraction of con */ + ulongest_t a = cols(alimit); + ulongest_t count; + ulongest_t i; + + /* if we can show alimit within 200 cols, do so */ + count = (a < 200) ? a + 1 : c; + + for(i = 0; i < count; i++) { + printf( (i == a) ? "A" + : (i < n) ? "n" + : (i < l) ? "L" + : (i < c) ? "_" + : " " + ); + } + printf("\n"); +} + + +/* print_M -- print count of bytes as Mebibytes or Megabytes + * + * Print as a whole number, "m" for the decimal point, and + * then the decimal fraction. + * + * Input: 208896 + * Output: (Mebibytes) 0m199 + * Output: (Megabytes) 0m209 + */ +#if 0 +#define bPerM (1UL << 20) /* Mebibytes */ +#else +#define bPerM (1000000UL) /* Megabytes */ +#endif +static void print_M(size_t bytes) +{ + size_t M; /* M thingies */ + double Mfrac; /* fraction of an M thingy */ + + M = bytes / bPerM; + Mfrac = (double)(bytes % bPerM); + Mfrac = (Mfrac / bPerM); + + printf("%1"PRIuLONGEST"m%03.f", (ulongest_t)M, Mfrac * 1000); +} + + +/* showStatsText -- present collection stats + * + * prints: + * Coll End 0m137[->0m019 14%-live] (0m211-not ) + */ +static void showStatsText(size_t notcon, size_t con, size_t live) +{ + double liveFrac = (double)live / (double)con; + + print_M(con); + printf("[->"); + print_M(live); + printf("% 3.f%%-live]", liveFrac * 100); + printf(" ("); + print_M(notcon); + printf("-not "); + printf(")\n"); +} + +/* get -- get messages + * + */ +static void get(mps_arena_t arena) +{ + mps_message_type_t type; + + while (mps_message_queue_type(&type, arena)) { + mps_message_t message; + static mps_clock_t mclockBegin = 0; + static mps_clock_t mclockEnd = 0; + mps_word_t *obj; + mps_word_t objind; + mps_addr_t objaddr; + + cdie(mps_message_get(&message, arena, type), + "get"); + + switch(type) { + case mps_message_type_gc_start(): { + mclockBegin = mps_message_clock(arena, message); + printf(" %5"PRIuLONGEST": (%5"PRIuLONGEST")", + mclockBegin, mclockBegin - mclockEnd); + printf(" Coll Begin (%s)\n", + mps_message_gc_start_why(arena, message)); + break; + } + case mps_message_type_gc(): { + size_t con = mps_message_gc_condemned_size(arena, message); + size_t notcon = mps_message_gc_not_condemned_size(arena, message); + /* size_t other = 0; -- cannot determine; new method reqd */ + size_t live = mps_message_gc_live_size(arena, message); + size_t alimit = mps_arena_reserved(arena); + + mclockEnd = mps_message_clock(arena, message); + + printf(" %5"PRIuLONGEST": (%5"PRIuLONGEST")", + mclockEnd, mclockEnd - mclockBegin); + printf(" Coll End "); + showStatsText(notcon, con, live); + if(rnd()==0) showStatsAscii(notcon, con, live, alimit); + break; + } + case mps_message_type_finalization(): { + mps_message_finalization_ref(&objaddr, arena, message); + obj = objaddr; + objind = DYLAN_INT_INT(DYLAN_VECTOR_SLOT(obj, 0)); + printf(" Finalization for object %"PRIuLONGEST" at %p\n", (ulongest_t)objind, objaddr); + break; + } + default: { + cdie(0, "message type"); + break; + } + } + + mps_message_discard(arena, message); + } +} + + +/* .catalog: The Catalog client: + * + * This is an MPS client for testing the MPS. It simulates + * converting a multi-page "Catalog" document from a page-description + * into a bitmap. + * + * The intention is that this task will cause memory usage that is + * fairly realistic (much more so than randomly allocated objects + * with random interconnections. The patterns in common with real + * clients are: + * - the program input and its task are 'fractal', with a + * self-similar hierarchy; + * - object allocation is prompted by each successive element of + * the input/task; + * - objects are often used to store a transformed version of the + * program input; + * - there may be several stages of transformation; + * - at each stage, the old object (holding the untransformed data) + * may become dead; + * - sometimes a tree of objects becomes dead once an object at + * some level of the hierarchy has been fully processed; + * - there is more than one hierarchy, and objects in different + * hierarchies interact. + * + * The entity-relationship diagram is: + * Catalog -< Page -< Article -< Polygon + * v + * | + * Palette --------------------< Colour + * + * The first hierarchy is a Catalog, containing Pages, each + * containing Articles (bits of artwork etc), each composed of + * Polygons. Each polygon has a single colour. + * + * The second hierarchy is a top-level Palette, containing Colours. + * Colours (in this client) are expensive, large objects (perhaps + * because of complex colour modelling or colour blending). + * + * The things that matter for their effect on MPS behaviour are: + * - when objects are allocated, and how big they are; + * - how the reference graph mutates over time; + * - how the mutator accesses objects (barrier hits). + */ + +enum { + CatalogRootIndex = 0, + CatalogSig = 0x0000CA2A, /* CATAlog */ + CatalogFix = 1, + CatalogVar = 10, + PageSig = 0x0000BA9E, /* PAGE */ + PageFix = 1, + PageVar = 100, + ArtSig = 0x0000A621, /* ARTIcle */ + ArtFix = 1, + ArtVar = 100, + PolySig = 0x0000B071, /* POLYgon */ + PolyFix = 1, + PolyVar = 100 +}; + +static void CatalogCheck(void) +{ + mps_word_t w; + void *Catalog, *Page, *Art, *Poly; + ulongest_t Catalogs = 0, Pages = 0, Arts = 0, Polys = 0; + int i, j, k; + + /* retrieve Catalog from root */ + Catalog = myrootExact[CatalogRootIndex]; + if(!Catalog) + return; + Insist(DYLAN_VECTOR_SLOT(Catalog, 0) == DYLAN_INT(CatalogSig)); + Catalogs += 1; + + for(i = 0; i < CatalogVar; i += 1) { + /* retrieve Page from Catalog */ + w = DYLAN_VECTOR_SLOT(Catalog, CatalogFix + i); + /* printf("Page = 0x%8x\n", (unsigned int) w); */ + if(w == DYLAN_INT(0)) + break; + Page = (void *)w; + Insist(DYLAN_VECTOR_SLOT(Page, 0) == DYLAN_INT(PageSig)); + Pages += 1; + + for(j = 0; j < PageVar; j += 1) { + /* retrieve Art from Page */ + w = DYLAN_VECTOR_SLOT(Page, PageFix + j); + if(w == DYLAN_INT(0)) + break; + Art = (void *)w; + Insist(DYLAN_VECTOR_SLOT(Art, 0) == DYLAN_INT(ArtSig)); + Arts += 1; + + for(k = 0; k < ArtVar; k += 1) { + /* retrieve Poly from Art */ + w = DYLAN_VECTOR_SLOT(Art, ArtFix + k); + if(w == DYLAN_INT(0)) + break; + Poly = (void *)w; + Insist(DYLAN_VECTOR_SLOT(Poly, 0) == DYLAN_INT(PolySig)); + Polys += 1; + } + } + } + printf("Catalog ok with: Catalogs: %"PRIuLONGEST", Pages: %"PRIuLONGEST", Arts: %"PRIuLONGEST", Polys: %"PRIuLONGEST".\n", + Catalogs, Pages, Arts, Polys); +} + + +/* CatalogDo -- make a Catalog and its tree of objects + * + * .catalog.broken: this code, when compiled with + * moderate optimization, may have ambiguous interior pointers but + * lack corresponding ambiguous base pointers to MPS objects. This + * means the interior pointers are unmanaged references, and the + * code goes wrong. The hack in poolamc.c#4 cures this, but not very + * nicely. For further discussion, see: + * + */ +static void CatalogDo(mps_arena_t arena, mps_ap_t ap) +{ + mps_word_t v; + void *Catalog, *Page, *Art, *Poly; + int i, j, k; + + die(make_dylan_vector(&v, ap, CatalogFix + CatalogVar), "Catalog"); + DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(CatalogSig); + Catalog = (void *)v; + + /* store Catalog in root */ + myrootExact[CatalogRootIndex] = Catalog; + get(arena); + + fflush(stdout); + CatalogCheck(); + + for(i = 0; i < CatalogVar; i += 1) { + die(make_dylan_vector(&v, ap, PageFix + PageVar), "Page"); + DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(PageSig); + Page = (void *)v; + + /* store Page in Catalog */ + DYLAN_VECTOR_SLOT(Catalog, CatalogFix + i) = (mps_word_t)Page; + get(arena); + + printf("Page %d: make articles\n", i); + fflush(stdout); + + for(j = 0; j < PageVar; j += 1) { + die(make_dylan_vector(&v, ap, ArtFix + ArtVar), "Art"); + DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(ArtSig); + Art = (void *)v; + + /* store Art in Page */ + DYLAN_VECTOR_SLOT(Page, PageFix + j) = (mps_word_t)Art; + get(arena); + + for(k = 0; k < ArtVar; k += 1) { + die(make_dylan_vector(&v, ap, PolyFix + PolyVar), "Poly"); + DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(PolySig); + Poly = (void *)v; + + /* store Poly in Art */ + DYLAN_VECTOR_SLOT(Art, ArtFix + k) = (mps_word_t)Poly; + /* get(arena); */ + } + } + } + fflush(stdout); + CatalogCheck(); +} + + +/* MakeThing -- make an object of the size requested (in bytes) + * + * Any size is accepted. MakeThing may round it up (MakeThing always + * makes a dylan vector, which has a minimum size of 8 bytes). Vector + * slots, if any, are initialized to DYLAN_INT(0). + * + * After making the object, calls get(), to retrieve MPS messages. + * + * make_dylan_vector [fmtdytst.c] says: + * size = (slots + 2) * sizeof(mps_word_t); + * That is: a dylan vector has two header words before the first slot. + */ +static void* MakeThing(mps_arena_t arena, mps_ap_t ap, size_t size) +{ + mps_word_t v; + ulongest_t words; + ulongest_t slots; + + words = (size + (sizeof(mps_word_t) - 1) ) / sizeof(mps_word_t); + if(words < 2) + words = 2; + + slots = words - 2; + die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); + get(arena); + + return (void *)v; +} + +static void BigdropSmall(mps_arena_t arena, mps_ap_t ap, size_t big, char small_ref) +{ + static ulongest_t keepCount = 0; + ulongest_t i; + + mps_arena_park(arena); + for(i = 0; i < 100; i++) { + (void) MakeThing(arena, ap, big); + if(small_ref == 'A') { + myrootAmbig[keepCount++ % myrootAmbigCOUNT] = MakeThing(arena, ap, 1); + } else if(small_ref == 'E') { + myrootExact[keepCount++ % myrootExactCOUNT] = MakeThing(arena, ap, 1); + } else { + cdie(0, "BigdropSmall: small must be 'A' or 'E'.\n"); + } + } +} + + +/* df -- diversity function + * + * Either deterministic based on "number", or 'random' (ie. call rnd). + */ + +static ulongest_t df(unsigned randm, ulongest_t number) +{ + if(randm == 0) { + return number; + } else { + return rnd(); + } +} + +static void Make(mps_arena_t arena, mps_ap_t ap, unsigned randm, unsigned keep1in, unsigned keepTotal, unsigned keepRootspace, unsigned sizemethod) +{ + unsigned keepCount = 0; + ulongest_t objCount = 0; + + Insist(keepRootspace <= myrootExactCOUNT); + + objCount = 0; + while(keepCount < keepTotal) { + mps_word_t v; + unsigned slots = 2; /* minimum */ + switch(sizemethod) { + case 0: { + /* minimum */ + slots = 2; + break; + } + case 1: { + slots = 2; + if(df(randm, objCount) % 10000 == 0) { + printf("*"); + slots = 300000; + } + break; + } + case 2: { + slots = 2; + if(df(randm, objCount) % 6661 == 0) { /* prime */ + printf("*"); + slots = 300000; + } + break; + } + default: { + printf("bad script command: sizemethod %u unknown.\n", sizemethod); + cdie(FALSE, "bad script command!"); + break; + } + } + die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); + DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(objCount); + DYLAN_VECTOR_SLOT(v, 1) = (mps_word_t)NULL; + objCount++; + if(df(randm, objCount) % keep1in == 0) { + /* keep this one */ + myrootExact[df(randm, keepCount) % keepRootspace] = (void*)v; + keepCount++; + } + get(arena); + } + printf(" ...made and kept: %u objects, storing cyclically in " + "first %u roots " + "(actually created %"PRIuLONGEST" objects, in accord with " + "keep-1-in %u).\n", + keepCount, keepRootspace, objCount, keep1in); +} + + +static void Rootdrop(char rank_char) +{ + ulongest_t i; + + if(rank_char == 'A') { + for(i = 0; i < myrootAmbigCOUNT; ++i) { + myrootAmbig[i] = NULL; + } + } else if(rank_char == 'E') { + for(i = 0; i < myrootExactCOUNT; ++i) { + myrootExact[i] = NULL; + } + } else { + cdie(0, "Rootdrop: rank must be 'A' or 'E'.\n"); + } +} + +#if 0 +#define stackwipedepth 50000 +static void stackwipe(void) +{ + unsigned iw; + ulongest_t aw[stackwipedepth]; + + /* http://xkcd.com/710/ */ + /* I don't want my friends to stop calling; I just want the */ + /* compiler to stop optimising away my code. */ + + /* Do you ever get two even numbers next to each other? Hmmmm :-) */ + for(iw = 0; iw < stackwipedepth; iw++) { + if((iw & 1) == 0) { + aw[iw] = 1; + } else { + aw[iw] = 0; + } + } + for(iw = 1; iw < stackwipedepth; iw++) { + if(aw[iw - 1] + aw[iw] != 1) { + printf("Errrr....\n"); + break; + } + } +} +#endif + +static void StackScan(mps_arena_t arena, int on) +{ + if(on) { + Insist(root_stackreg == NULL); + die(mps_root_create_reg(&root_stackreg, arena, + mps_rank_ambig(), (mps_rm_t)0, stack_thr, + mps_stack_scan_ambig, stack_start, 0), + "root_stackreg"); + Insist(root_stackreg != NULL); + } else { + Insist(root_stackreg != NULL); + mps_root_destroy(root_stackreg); + root_stackreg = NULL; + Insist(root_stackreg == NULL); + } +} + + +/* checksi -- check count of sscanf items is correct + */ + +static void checksi(int si, int si_shouldBe, const char *script, const char *scriptAll) +{ + if(si != si_shouldBe) { + printf("bad script command (sscanf found wrong number of params) %s (full script %s).\n", script, scriptAll); + cdie(FALSE, "bad script command!"); + } +} + +/* testscriptC -- actually runs a test script + * + */ +static void testscriptC(mps_arena_t arena, mps_ap_t ap, const char *script) +{ + const char *scriptAll = script; + int si, sb; /* sscanf items, sscanf bytes */ + + while(*script != '\0') { + switch(*script) { + case 'C': { + si = sscanf(script, "Collect%n", + &sb); + checksi(si, 0, script, scriptAll); + script += sb; + printf(" Collect\n"); + /* stackwipe(); */ + mps_arena_collect(arena); + mps_arena_release(arena); + break; + } + case 'T': { + si = sscanf(script, "Transform%n", + &sb); + checksi(si, 0, script, scriptAll); + script += sb; + printf(" Transform\n"); + Transform(arena, ap); + break; + } + case 'K': { + si = sscanf(script, "Katalog()%n", + &sb); + checksi(si, 0, script, scriptAll); + script += sb; + printf(" Katalog()\n"); + CatalogDo(arena, ap); + break; + } + case 'B': { + ulongest_t big = 0; + char small_ref = ' '; + si = sscanf(script, "BigdropSmall(big %"SCNuLONGEST", small %c)%n", + &big, &small_ref, &sb); + checksi(si, 2, script, scriptAll); + script += sb; + printf(" BigdropSmall(big %"PRIuLONGEST", small %c)\n", big, small_ref); + BigdropSmall(arena, ap, big, small_ref); + break; + } + case 'M': { + unsigned randm = 0; + unsigned keep1in = 0; + unsigned keepTotal = 0; + unsigned keepRootspace = 0; + unsigned sizemethod = 0; + si = sscanf(script, "Make(random %u, keep-1-in %u, keep %u, rootspace %u, sizemethod %u)%n", + &randm, &keep1in, &keepTotal, &keepRootspace, &sizemethod, &sb); + checksi(si, 5, script, scriptAll); + script += sb; + printf(" Make(random %u, keep-1-in %u, keep %u, rootspace %u, sizemethod %u).\n", + randm, keep1in, keepTotal, keepRootspace, sizemethod); + Make(arena, ap, randm, keep1in, keepTotal, keepRootspace, sizemethod); + break; + } + case 'R': { + char drop_ref = ' '; + si = sscanf(script, "Rootdrop(rank %c)%n", + &drop_ref, &sb); + checksi(si, 1, script, scriptAll); + script += sb; + printf(" Rootdrop(rank %c)\n", drop_ref); + Rootdrop(drop_ref); + break; + } + case 'S': { + unsigned on = 0; + si = sscanf(script, "StackScan(%u)%n", + &on, &sb); + checksi(si, 1, script, scriptAll); + script += sb; + printf(" StackScan(%u)\n", on); + StackScan(arena, on != 0); + break; + } + case 'Z': { + ulongest_t s0; + si = sscanf(script, "ZRndStateSet(%"SCNuLONGEST")%n", + &s0, &sb); + checksi(si, 1, script, scriptAll); + script += sb; + printf(" ZRndStateSet(%"PRIuLONGEST")\n", s0); + rnd_state_set((unsigned long)s0); + break; + } + case ' ': + case ',': + case '.': { + script++; + break; + } + default: { + printf("unknown script command '%c' (script %s).\n", + *script, scriptAll); + cdie(FALSE, "unknown script command!"); + return; + } + } + get(arena); + } + +} + + +/* testscriptB -- create pools and objects; call testscriptC + * + * Is called via mps_tramp, so matches mps_tramp_t function prototype, + * and use trampDataStruct to pass parameters. + */ + +typedef struct trampDataStruct { + mps_arena_t arena; + mps_thr_t thr; + const char *script; +} trampDataStruct; + +static void *testscriptB(void *arg, size_t s) +{ + trampDataStruct trampData; + mps_arena_t arena; + mps_thr_t thr; + const char *script; + mps_fmt_t fmt; + mps_chain_t chain; + mps_pool_t amc; + int i; + mps_root_t root_table_Ambig; + mps_root_t root_table_Exact; + mps_ap_t ap; + void *stack_starts_here; /* stack scanning starts here */ + + Insist(s == sizeof(trampDataStruct)); + trampData = *(trampDataStruct*)arg; + arena = trampData.arena; + thr = trampData.thr; + script = trampData.script; + + die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create"); + die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); + die(mps_pool_create(&amc, arena, mps_class_amc(), fmt, chain), + "pool_create amc"); + + for(i = 0; i < myrootAmbigCOUNT; ++i) { + myrootAmbig[i] = NULL; + } + die(mps_root_create_table(&root_table_Ambig, arena, mps_rank_ambig(), (mps_rm_t)0, + myrootAmbig, (size_t)myrootAmbigCOUNT), + "root_create - ambig"); + + for(i = 0; i < myrootExactCOUNT; ++i) { + myrootExact[i] = NULL; + } + die(mps_root_create_table(&root_table_Exact, arena, mps_rank_exact(), (mps_rm_t)0, + myrootExact, (size_t)myrootExactCOUNT), + "root_create - exact"); + + die(mps_ap_create(&ap, amc, mps_rank_exact()), "ap_create"); + + /* root_stackreg: stack & registers are ambiguous roots = mutator's workspace */ + stack_start = &stack_starts_here; + stack_thr = thr; + die(mps_root_create_reg(&root_stackreg, arena, + mps_rank_ambig(), (mps_rm_t)0, stack_thr, + mps_stack_scan_ambig, stack_start, 0), + "root_stackreg"); + + + mps_message_type_enable(arena, mps_message_type_gc_start()); + mps_message_type_enable(arena, mps_message_type_gc()); + mps_message_type_enable(arena, mps_message_type_finalization()); + + testscriptC(arena, ap, script); + + printf(" Destroy roots, pools, arena etc.\n\n"); + mps_root_destroy(root_stackreg); + mps_ap_destroy(ap); + mps_root_destroy(root_table_Exact); + mps_root_destroy(root_table_Ambig); + mps_pool_destroy(amc); + mps_chain_destroy(chain); + mps_fmt_destroy(fmt); + + return NULL; +} + + +/* testscriptA -- create arena, thr; call testscriptB + */ +static void testscriptA(const char *script) +{ + mps_arena_t arena; + int si, sb; /* sscanf items, sscanf bytes */ + ulongest_t arenasize = 0; + mps_thr_t thr; + trampDataStruct trampData; + + si = sscanf(script, "Arena(size %"SCNuLONGEST")%n", &arenasize, &sb); + cdie(si == 1, "bad script command: Arena(size %%"PRIuLONGEST")"); + script += sb; + printf(" Create arena, size = %"PRIuLONGEST".\n", arenasize); + + /* arena */ + die(mps_arena_create(&arena, mps_arena_class_vm(), arenasize), + "arena_create"); + + /* thr: used to stop/restart multiple threads */ + die(mps_thread_reg(&thr, arena), "thread"); + + /* call testscriptB! */ + trampData.arena = arena; + trampData.thr = thr; + trampData.script = script; + testscriptB(&trampData, sizeof trampData); + + mps_thread_dereg(thr); + mps_arena_destroy(arena); +} + + +/* main -- runs various test scripts + * + */ +int main(int argc, char *argv[]) +{ + randomize(argc, argv); + mps_lib_assert_fail_install(assert_die); + + /* 1<<19 == 524288 == 1/2 Mebibyte */ + /* 16<<20 == 16777216 == 16 Mebibyte */ + + testscriptA("Arena(size 500000000), " + "Transform" + /*", Collect, Rootdrop(rank E), Collect, Collect"*/ + "."); + + printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); + return 0; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (c) 2001-2013 Ravenbrook Limited . + * All rights reserved. This is an open source license. Contact + * Ravenbrook for commercial licensing options. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Redistributions in any form must be accompanied by information on how + * to obtain complete source code for this software and any accompanying + * software that uses this software. The source code must either be + * included in the distribution or be available for no more than the cost + * of distribution plus a nominal fee, and must be freely redistributable + * under reasonable conditions. For an executable file, complete source + * code means the source code for all modules it contains. It does not + * include source code for modules or files that typically accompany the + * major components of the operating system on which the executable file + * runs. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/design/transform.txt b/design/transform.txt new file mode 100644 index 0000000000..68122ca759 --- /dev/null +++ b/design/transform.txt @@ -0,0 +1,126 @@ +MPS TRANSFORM DESIGN +==================== +Richard Brooksby, Ravenbrook Limited, 2012-09-04 + + +1. Introduction +--------------- + +This document describes the Transform mechanism of the Memory Pool +System. Transforms allow the client code to replace a set of object +references on the heap. Transforms exist to satisfy a requirement from +Configura, and are not intended to become part of the open source +MPS. + +The readership of this document is any developer intending to modify the +Transform implementation. + +This document is Configura client confidential. + + +2. Background +------------- + +Göran Rydqvist of Configura originally expressed the requirement for the +MPS to support the change of layout of objects in CET [[Crab +2010-02-25][]]. Ravenbrook proposed several methods [[RHSK +2010-09-21][]] but Configura selected idea 1: + +> Make all objects "direct", without IOHeaders. If you need to add fields, +> then use a special new MPS function (that doesn't exist yet): +> +> mps_arena_transform_objects(&my_transform_function); +> +> This traverses the object graph, lets your transform_function basically +> "realloc()" the field-block, and MPS fixes up all references from other +> objects to point to the new field-block. +> +> Unfortunately, this idea is probably killed off by ambiguous references +> :-(. You could only run the patch if you could *guarantee* there are no +> ambiguous refs you want. In other words, any object refs on the stack +> would become instant death (or worse: subtle slow death :-). Therefore we +> don't really like this idea (unfortunately). There are safer and simpler +> ways to do it, we think... + +An initial implementation was made by RHSK and released to Configura as +"experimental", however Configura put it into production. + +During work on adapting the MPS to 64-bit Windows, RB reformed and +reimplemented transforms based on RHSK's original work. + + +3. Overview +----------- + +The client program builds a table mapping "old" references to "new" ones +in a `Transform` object. This is then "applied", causing a garbage +collection trace in which the fix function is substituted by +`transformFix`, which spots "old" references and replaces them with +"new" ones, in addition to applying the usual garbage collection fix +function. + + +4. NOT YET WRITTEN +------------------ + +Points to cover: + +* Ambiguous references and aborting the transform + + * How ambiguous references are avoided using stackAtEnter + +* How the implementation is an add-on to the MPS + +* How the code is kept separate from the mainstream MPS + +* Why it has its own hash table implementation (no good reason) + +* Why it does a garbage collection and not just a transforming scan + + * Nice side-effect is that "old" objects are killed + +* Why the arena must be parked + + + +A. References +------------- + +[Crab 2010-02-25]: http://info.ravenbrook.com/mail/2010/02/25/16-35-45/0/ +[Crab 2010-02-25] "Incremental object" (email); Göran Rydqvist; +Configura; 2010-02-25; +. + +[RHSK 2010-09-21]: http://info.ravenbrook.com/mail/2010/09/21/16-54-59/0/ +[RHSK 2010-09-21] "Incremental object ideas" (email); Richard Kistruck; +Ravenbrook Limited; 2010-09-21; +. + +[Crab 2010-09-22]: http://info.ravenbrook.com/mail/2010/09/22/09-27-53/0/ +[Crab 2010-09-22] "Incremental object ideas" (email); Göran Rydqvist; +Configura; 2010-09-22; +. + + +B. Document History +------------------- + +* 2012-09-04 [RB](mailto:rb@ravenbrook.com) First draft. + + +C. Copyright and Licence +------------------------ + +This document is copyright © 2012 [Ravenbrook Limited](http://www.ravenbrook.com). All rights reserved. This is an +open source license. Contact Ravenbrook for commercial licensing +options. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Redistributions in any form must be accompanied by information on how to obtain complete source code for the 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/manual/source/custom/cet/extension.rst b/manual/source/custom/cet/extension.rst new file mode 100644 index 0000000000..5c13503e68 --- /dev/null +++ b/manual/source/custom/cet/extension.rst @@ -0,0 +1,108 @@ +.. index:: + single: arena extension callbacks; introduction + single: extension callbacks; introduction + single: arena contraction callbacks; introduction + single: contraction callbacks; introduction + +.. _topic-extension: + +Arena extension callbacks +========================= + +There are situations in which the :term:`client program` needs to be +informed about the chunks of address space that an :term:`arena` is +managing. To support this, the MPS allows the client program to +specify two callback functions when creating a :term:`virtual memory +arena`: one function is called when the arena is *extended* (that is, +when it acquires a new chunk of address space from the operating +system), and the other when the arena is *contracted* (that is, when +it returns a chunk of address space to the operating system). + +The use case that this feature is designed to support is debugging of dynamically generated code in 64-bit Windows. `Microsoft explains `_: + +.. _`MSDN`: http://msdn.microsoft.com/en-us/library/windows/desktop/ms680595.aspx + + Function tables are used on 64-bit Windows to determine how to + unwind or walk the stack. These tables are usually generated by + the compiler and stored as part of the image. However, + applications must provide the function table for dynamically + generated code. + +An application may install a dynamic function table by calling ``RtlInstallFunctionTableCallback()``, passing the region of memory in which the dynamically generated functions can be found, and may later delete the table by calling ``RtlDeleteFunctionTable()``. + +So if the client program is storing dynamically generated functions in MPS-managed memory, then it could define callback functionss that install and delete the function table callback for the dynamically generated code, like this:: + + void arena_extended(mps_arena_t arena, void *base, size_t size) + { + RtlInstallFunctionTableCallback(...); + } + + void arena_contracted(mps_arena_t arena, void *base, size_t size) + { + RtlDeleteFunctionTable(...); + } + +and then pass these two functions using :term:`keyword arguments` to :c:func:`mps_arena_create_k`:: + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_EXTENDED, (mps_fun_t)arena_extended); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_CONTRACTED, (mps_fun_t)arena_contracted); + /* ... other keyword arguments ... */ + MPS_ARGS_DONE(args); + res = mps_arena_create_k(&arena, mps_arena_class_vm(), args); + } MPS_ARGS_END(args); + + +.. index:: + single: arena extension callbacks; interface + single: extension callbacks; interface + single: arena contraction callbacks; interface + single: contraction callbacks; interface + +Arena extension callback interface +---------------------------------- + +:: + + #include "mpscvm.h" + +.. c:macro:: MPS_KEY_ARENA_EXTENDED + + A :term:`keyword argument` (of type :c:type:`mps_fun_t`) to + :c:func:`mps_arena_create_k` whose value specifies a function that + will be called when the arena is *extended*: that is, when it + acquires a new chunk of address space from the operating system. + + The function receives three arguments: ``arena`` (the arena being + extended), ``base`` (the base address of the new chunk of address + space), and ``size`` (the size of the new chunk of address space, + in bytes). + + The function must not call any function in the MPS, and must not + access any memory managed by the MPS. + + .. note:: + + This keyword argument is only supported by :term:`virtual + memory arenas`. + +.. c:macro:: MPS_KEY_ARENA_CONTRACTED + + A :term:`keyword argument` (of type :c:type:`mps_fun_t`) to + :c:func:`mps_arena_create_k` whose value specifies a function that + will be called when the arena is *contracted*: that is, when it + finishes with a chunk of address space and returns it to the + operating system. + + The function receives three arguments: ``arena`` (the arena being + contracted), ``base`` (the base address of the old chunk of + address space), and ``size`` (the size of the old chunk of address + space, in bytes). + + The function must not call any function in the MPS, and must not + access any memory managed by the MPS. + + .. note:: + + This keyword argument is only supported by :term:`virtual + memory arenas`. diff --git a/manual/source/custom/cet/hasharray.rst b/manual/source/custom/cet/hasharray.rst new file mode 100644 index 0000000000..06097cb7f8 --- /dev/null +++ b/manual/source/custom/cet/hasharray.rst @@ -0,0 +1,61 @@ + +.. index:: + single: hash array; introduction + +Hash arrays +=========== + +The :term:`location dependency` feature of the MPS allows the +:term:`client program` to implement address-based hash tables in the +presence of a :term:`moving memory manager`, re-hashing the tables +when the addresses they contain might have moved. + +However, when a frequently-used hash table grows large enough, the following sequence of events may take place: + +1. The hash table discovers that its location dependency is stale. + +2. A new array is allocated to contain the re-hashed keys. + +3. The new array is large enough to push the *new size* of the + :term:`nursery space` (that is, the amount of newly allocated + memory since the last collection in the first :term:`generation` in + the :term:`generation chain` for the pool containing the array) + close to its capacity. + +4. A small amount of additional allocation causes the new size of the + nursery generation to exceed its capacity, which causes the MPS to + start a new collection of that generation. This in turn causes the + hash table to become stale again. + +When the hash table reaches this critical size, the client program may find that a large fraction of its time is being spent re-hashing the table. + +In order to avoid this happening, the MPS provides a mechanism allowing you specify that the newly allocated array does not contibute to the new size of the nursery space: this cuts off the vicious cycle at step 3. + +See :ref:`topic-collection-schedule` for an explanation of the *new +size* of a generation, and how the MPS uses this to determine when to +start a collection of that generation. + + +.. index:: + single: hash array; interface + +Hash array interface +-------------------- + +:: + + #include "mpscvm.h" + +.. c:macro:: MPS_KEY_AP_HASH_ARRAYS + + A :term:`keyword argument` (of type :c:type:`mps_bool_t`, + defaulting to false) to :c:func:`mps_ap_create_k`. If true, it + specifies that blocks allocated from the allocation point do not + contribute to the *new size* of the :term:`nursery space` for the + purposes of deciding whether to start a collection of that + generation. + + .. note:: + + This keyword argument is only supported by :ref:`pool-amc` + pools. diff --git a/manual/source/custom/cet/index.rst b/manual/source/custom/cet/index.rst new file mode 100644 index 0000000000..f274a6cd2b --- /dev/null +++ b/manual/source/custom/cet/index.rst @@ -0,0 +1,11 @@ +.. _custom_cet: + +Custom features for CET +*********************** + +.. toctree:: + :numbered: + + transform + extension + hasharray diff --git a/manual/source/custom/cet/transform.rst b/manual/source/custom/cet/transform.rst new file mode 100644 index 0000000000..aa9c27a6d8 --- /dev/null +++ b/manual/source/custom/cet/transform.rst @@ -0,0 +1,132 @@ +.. Sources: + + ``_ + +.. index:: + single: transform; introduction + +Transforms +========== + +In a long-running interactive system, it may be desirable to change the format of live objects. In some programming languages (notably Smalltalk), when the programmer edits a class definition, objects belonging to the class must be updated so that they are valid instances of the redefined class. This may involve adding or removing fields from each instance and so changing the size of the allocated objects. + +If the object has grown as a result of the redefinition, this redefinition can't be done in-place, so what actually happens is that for each instance of the old version of the class, a corresponding instance of the new version of the class is created, and all references to the old instance are rewritten to refer to the new instance. And discovering "all references" to an object is a task that falls to the garbage collector. + +*Transforms* are a general mechanism by which the client program requests the MPS to replace references to one set of objects (the *old* objects) with references to another (the *new* objects). The MPS performs this task by carrying out a complete garbage collection, in the course of which all references to old objects are discovered and substituted with references to the corresponding new object. + + +Transform cautions +------------------ + +1. The arena must be :term:`parked ` (for example, by + calling :c:func:`mps_arena_park`) before creating the transform and + not :term:`unclamped ` before applying the + transform. + +2. A transform cannot be applied if there is an :term:`ambiguous + reference` to any of the old objects. (Because the MPS cannot know + whether or not the reference should be updated to point to the new + object.) + + +.. index:: + single: transform; interface + +Transform interface +------------------- + +:: + + #include "mpstr.h" + + +.. c:type:: mps_transform_t + + The type of transforms. A transform represents a mapping from + *old* objects to *new* objects. + + +.. c:function:: mps_res_t mps_transform_create(mps_transform_t *transform_o, mps_arena_t arena) + + Create an empty transform. + + ``transform_o`` points to a location that will hold the address of + the new transform. + + ``arena`` is the arena in which to create the transform. + + :c:func:`mps_transform_create` returns :c:macro:`MPS_RES_OK` if + successful. The MPS may exhaust some resource in the course of + :c:func:`mps_transform_create` and will return an appropriate + :term:`result code` if so. + + .. note:: + + The arena must be :term:`parked ` (for example, + by calling :c:func:`mps_arena_park`) before creating a + transform, and if :c:func:`mps_transform_apply` is called on + a transform, it must be called before the arena is + :term:`unclamped `. + + +.. c:function:: mps_res_t mps_transform_add_oldnew(mps_transform_t transform, mps_addr_t *old_array, mps_addr_t *new_array, size_t count) + + Add mappings from old to new objects to a transform. + + ``transform`` is the transform to which the mappings will be added. + + ``old_array`` points to an array of references to old objects. + + ``new_array`` points to an array of references to new objects. + + ``count`` is the number of references in each array. + + :c:func:`mps_transform_add_oldnew` returns :c:macro:`MPS_RES_OK` + if successful. The MPS may exhaust some resource in the course of + :c:func:`mps_transform_add_oldnew` and will return an appropriate + :term:`result code` if so. + + .. note:: + + An old object must be added at most once to a transform. + + +.. c:function:: mps_res_t mps_transform_apply(mps_bool_t *applied_o, mps_transform_t transform) + + Attempt to apply a transform. + + ``applied_o`` points to a location that will hold a Boolean + indicating whether or not the transform was applied. + + ``transform`` is the transform to apply. + + If the arena is currently incapable of applying the transform, + then an appropriate :term:`result code` is returned, and the + location pointed to by ``applied_o`` is not updated. Possible + causes include (but are not limited to) the arena not being in the + :term:`parked state` (in which case the result code is + :c:macro:`MPS_RES_LIMIT`), or a collection having taken place + since ``transform`` was created (in which case the result code is + :c:macro:`MPS_RES_PARAM`). + + If the arena is *capable* of applying the transform, then the MPS + carries out a garbage collection, the arena is left in the + :term:`parked state`, :c:func:`mps_transform_apply` returns + :c:macro:`MPS_RES_OK`, and the location pointed to by + ``applied_o`` is updated. + + If in the course of the ambiguous reference was discovered, then + the transform is aborted and ``*applied_o`` is set to false. In + this case, *no* references to the old objects are updated. (That + is, either *all* of the transform is applied, or *none* of it.) + + If the transform was successfully applied, it is destroyed (as if + :c:func:`mps_transform_destroy` had been called). + + +.. c:function:: void mps_transform_destroy(mps_transform_t transform) + + Destroy a transform. + + ``transform`` is the transform to destroy. + diff --git a/manual/source/design/index.rst b/manual/source/design/index.rst index 7e568a89d7..5087eeeefb 100644 --- a/manual/source/design/index.rst +++ b/manual/source/design/index.rst @@ -45,6 +45,7 @@ Design testthr thread-manager thread-safety + transform type version-library vm diff --git a/manual/source/index.rst b/manual/source/index.rst index 5d61d583c2..b70fe2627f 100644 --- a/manual/source/index.rst +++ b/manual/source/index.rst @@ -7,6 +7,7 @@ Memory Pool System guide/index topic/index pool/index + custom/cet/index design/index design/old diff --git a/procedure/release-build.rst b/procedure/release-build.rst index eff46a9299..dc7bf55c96 100644 --- a/procedure/release-build.rst +++ b/procedure/release-build.rst @@ -199,10 +199,10 @@ On a Unix (including macOS) machine: p4 -c $CLIENT client -d $CLIENT rm -rf /tmp/$CLIENT -#. Edit the index of releases (``release/index.html``) and add the +#. Edit the index of releases (``release/index.*``) and add the release to the table, in a manner consistent with previous releases. -#. Edit the index of versions (``version/index.html``) and add the +#. Edit the index of versions (``version/index.*``) and add the release to the list of releases for *VERSION*, in a manner consistent with previous releases. diff --git a/tool/testcases.txt b/tool/testcases.txt index b19de92d12..787ba139d0 100644 --- a/tool/testcases.txt +++ b/tool/testcases.txt @@ -43,6 +43,7 @@ teletest =N interactive walkt0 zcoll =L zmess +ztfm =L ============= ================ ========================================== Key to flags From b1c30975d91a8ab871ca4d94de1754d5ee44ef03 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sun, 23 Jan 2022 21:08:47 +0000 Subject: [PATCH 2/3] Make transforms part of the public MPS. * Move transforms sources to the core sections of the makefiles. * Move function declarations to the public header mps.h. * Update copyright notices for transforms code. * Remove references to Configura from the comments. * Remove trailing whitespace. * Translate design to reStructuredText. * Move documentation to reference section of manual. * Add warning about unsuitability when ambiguous references may exist. --- code/comm.gmk | 13 +- code/commpre.nmk | 13 +- code/mps.c | 3 - code/mps.h | 9 + code/mpsitr.c | 58 ++--- code/mpstr.h | 66 ----- code/trans.c | 68 ++--- code/trans.h | 52 ++-- code/ztfm.c | 235 ++++++++---------- design/transform.txt | 173 +++++++------ manual/source/glossary/t.rst | 9 + manual/source/release.rst | 5 + manual/source/topic/index.rst | 1 + .../{custom/cet => topic}/transform.rst | 91 ++++--- 14 files changed, 352 insertions(+), 444 deletions(-) delete mode 100644 code/mpstr.h rename manual/source/{custom/cet => topic}/transform.rst (56%) diff --git a/code/comm.gmk b/code/comm.gmk index ec8f93bd22..daaf0e6cfb 100644 --- a/code/comm.gmk +++ b/code/comm.gmk @@ -191,6 +191,7 @@ MPMCOMMON = \ meter.c \ mpm.c \ mpsi.c \ + mpsitr.c \ nailboard.c \ policy.c \ pool.c \ @@ -214,6 +215,7 @@ MPMCOMMON = \ trace.c \ traceanc.c \ tract.c \ + trans.c \ tree.c \ version.c \ vm.c \ @@ -221,10 +223,6 @@ MPMCOMMON = \ POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC) MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH) -# Custom CET extensions go here: - -MPM += mpsitr.c trans.c - # These map the source file lists onto object files and dependency files # in the platform/variety directory. @@ -297,7 +295,8 @@ TEST_TARGETS=\ teletest \ walkt0 \ zcoll \ - zmess + zmess \ + ztfm # This target records programs that we were once able to build but # can't at the moment: @@ -305,10 +304,6 @@ TEST_TARGETS=\ UNBUILDABLE_TARGETS=\ replay # depends on the EPVM pool -# Add tests for the custom CET build here: - -TEST_TARGETS += ztfm - ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS) diff --git a/code/commpre.nmk b/code/commpre.nmk index 3ff41b1766..61e076b37c 100644 --- a/code/commpre.nmk +++ b/code/commpre.nmk @@ -98,7 +98,8 @@ TEST_TARGETS=\ teletest.exe \ walkt0.exe \ zcoll.exe \ - zmess.exe + zmess.exe \ + ztfm.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 @@ -113,10 +114,6 @@ OPTIONAL_TARGETS=mpseventsql.exe UNBUILDABLE_TARGETS=replay.exe -# Add tests for the custom CET build here: - -TEST_TARGETS=$(TEST_TARGETS) ztfm.exe - ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS) @@ -149,6 +146,7 @@ MPMCOMMON=\ [meter] \ [mpm] \ [mpsi] \ + [mpsitr] \ [nailboard] \ [policy] \ [pool] \ @@ -173,6 +171,7 @@ MPMCOMMON=\ [trace] \ [traceanc] \ [tract] \ + [trans] \ [tree] \ [version] \ [vm] \ @@ -193,10 +192,6 @@ TESTTHR = [testthrw3] POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC) MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH) -# Custom CET extensions go here: - -MPMCOMMON = $(MPMCOMMON) [mpsitr] [trans] - # CHECK PARAMETERS # diff --git a/code/mps.c b/code/mps.c index 5a761c23a7..378965605a 100644 --- a/code/mps.c +++ b/code/mps.c @@ -80,9 +80,6 @@ #include "failover.c" #include "vm.c" #include "policy.c" - -/* Configura CET customisations */ - #include "trans.c" #include "mpsitr.c" diff --git a/code/mps.h b/code/mps.h index 4c56265c2e..2514236351 100644 --- a/code/mps.h +++ b/code/mps.h @@ -833,6 +833,15 @@ extern mps_res_t _mps_fix2(mps_ss_t, mps_addr_t *); MPS_END +/* Transforms interface. */ + +typedef struct mps_transform_s *mps_transform_t; +extern mps_res_t mps_transform_create(mps_transform_t *, mps_arena_t); +extern mps_res_t mps_transform_add_oldnew(mps_transform_t, mps_addr_t *, mps_addr_t *, size_t); +extern mps_res_t mps_transform_apply(mps_bool_t *, mps_transform_t); +extern void mps_transform_destroy(mps_transform_t); + + #endif /* mps_h */ diff --git a/code/mpsitr.c b/code/mpsitr.c index 866731fd3d..522d809969 100644 --- a/code/mpsitr.c +++ b/code/mpsitr.c @@ -1,16 +1,14 @@ /* mpsitr.c: MEMORY POOL SYSTEM C INTERFACE LAYER TO TRANSFORMS * * $Id$ - * Copyright 2011 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2011-2022 Ravenbrook Limited. See end of file for license. * - * .purpose: This code bridges between the MPS interface to C to Transforms - * and the internal implementation of Transforms. It is - * analogous to the MPS C Interface Layer , but for the Transforms - * extension. + * .purpose: This code bridges between the MPS interface to transforms in + * and the internal implementation of Transforms in . */ #include "mpm.h" -#include "mpstr.h" +#include "mps.h" #include "trans.h" #include "ss.h" @@ -95,41 +93,29 @@ void mps_transform_destroy(mps_transform_t transform) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2011 Ravenbrook Limited . - * All rights reserved. This is an open source license. Contact - * Ravenbrook for commercial licensing options. - * + * Copyright (C) 2011-2022 Ravenbrook Limited . + * * 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. - * + * 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. - * + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * * 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. + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR 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/mpstr.h b/code/mpstr.h deleted file mode 100644 index d718e4ed1e..0000000000 --- a/code/mpstr.h +++ /dev/null @@ -1,66 +0,0 @@ -/* mpstr.h: RAVENBROOK MEMORY POOL SYSTEM C INTERFACE TO TRANSFORMS - * - * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. - * - * .readership: customers, MPS developers. - */ - -#ifndef mpstr_h -#define mpstr_h - -#include "mps.h" - -typedef struct mps_transform_s *mps_transform_t; - -extern mps_res_t mps_transform_create(mps_transform_t *, mps_arena_t); - -extern mps_res_t mps_transform_add_oldnew(mps_transform_t, mps_addr_t *, mps_addr_t *, size_t); - -extern mps_res_t mps_transform_apply(mps_bool_t *, mps_transform_t); - -extern void mps_transform_destroy(mps_transform_t); - -#endif /* mpstr_h */ - - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2011 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/trans.c b/code/trans.c index 565b15800d..ef006a4f02 100644 --- a/code/trans.c +++ b/code/trans.c @@ -1,9 +1,7 @@ -/* trans.c: RAVENBROOK MEMORY POOL SYSTEM TRANSFORMS +/* trans.c: TRANSFORMS IMPLEMENTATION * * $Id$ - * Copyright 2011-2018 Ravenbrook Limited. See end of file for license. - * - * This code is specific to Configura. + * Copyright 2011-2022 Ravenbrook Limited. See end of file for license. * * A transform is a special kind of garbage collection that replaces references * to a set of objects. The transform is piggybacked onto a garbage @@ -230,13 +228,9 @@ static Res transformFix(Seg seg, ScanState ss, Ref *refIO) if (TableLookup(&refNew, transform->oldToNew, (Word)ref)) { if (ss->rank == RankAMBIG) { - /* We rely on the fact that all ambiguous references are fixed before - any others, so no references will have transformed by the time we - abort. - NOTE: Configura CVM prints a message and exits if a transform - fails. See Configura's - //depot/project/cet/kernel/internal/version3.2/cvm/c2/updateHeap2.m#4 - line 133.*/ + /* We rely on the fact that ambiguous references are fixed + first, so that no exact references have been transformed + yet. */ transform->aborted = TRUE; } else { /* NOTE: We could fix refNew in the table before copying it, @@ -296,9 +290,7 @@ Res TransformApply(Bool *appliedReturn, Transform transform) /* If there have been any flips since the transform was created, the old and new pointers will be invalid, since they are not scanned as roots. - NOTE: Configura CVM parks the arena before adding references. See - //depot/project/cet/kernel/internal/version3.2/cvm/c2/updateHeap2.m#4 - line 114. */ + The client program must park the arena before applying the transform. */ if (transform->epoch != ArenaEpoch(arena)) return ResPARAM; @@ -357,41 +349,29 @@ Res TransformApply(Bool *appliedReturn, Transform transform) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2011-2018 Ravenbrook Limited . - * All rights reserved. This is an open source license. Contact - * Ravenbrook for commercial licensing options. - * + * Copyright (C) 2011-2022 Ravenbrook Limited . + * * 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. - * + * 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. - * + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * * 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. + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR 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/trans.h b/code/trans.h index 4a19f5fe92..a572138601 100644 --- a/code/trans.h +++ b/code/trans.h @@ -1,7 +1,7 @@ -/* trans.h: RAVENBROOK MEMORY POOL SYSTEM TRANSFORMS +/* trans.h: TRANSFORMS INTERFACE * * $Id$ - * Copyright 2011 Ravenbrook Limited. See end of file for license. + * Copyright 2011-2022 Ravenbrook Limited. See end of file for license. */ #ifndef trans_h @@ -35,41 +35,29 @@ extern Arena TransformArena(Transform transform); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2011 Ravenbrook Limited . - * All rights reserved. This is an open source license. Contact - * Ravenbrook for commercial licensing options. - * + * Copyright (C) 2011-2022 Ravenbrook Limited . + * * 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. - * + * 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. - * + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * * 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. + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR 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/ztfm.c b/code/ztfm.c index 8dc81a5be2..2f2c237692 100644 --- a/code/ztfm.c +++ b/code/ztfm.c @@ -1,27 +1,7 @@ -/* ztfm.c: Transform test +/* ztfm.c: Transforms test * * $Id$ - * Copyright (c) 2010 Ravenbrook Limited. See end of file for license. - * Portions copyright (C) 2002 Global Graphics Software. - * - * OBJECTIVE - * - * - * DESIGN OVERVIEW - * - * CODE OVERVIEW - * - * DEPENDENCIES - * - * This test uses the dylan object format, but the reliance on this - * particular format is not great and could be removed. - * - * - * BUGS, FUTURE IMPROVEMENTS, ETC - * - * HISTORY - * - * This code was created by first copying . + * Copyright (c) 2011-2022 Ravenbrook Limited. See end of file for license. */ #include "fmtdy.h" @@ -30,7 +10,6 @@ #include "mpsavm.h" #include "mpscamc.h" #include "mpslib.h" -#include "mpstr.h" #include "testlib.h" #include /* printf */ @@ -93,28 +72,28 @@ struct node_t { /* Tour -- a particular journey to visit to every node in the world * - * A tour starts with the node at world[0], tours the graph reachable - * from it, then any further bits of graph reachable from world[1], + * A tour starts with the node at world[0], tours the graph reachable + * from it, then any further bits of graph reachable from world[1], * and so on. * - * As it does so, the tour computes a tourReport, characterising the + * As it does so, the tour computes a tourReport, characterising the * state of everything reachable (from world) in the form a few numbers. * - * Each tour explores the subgraph rooted at each node exactly once. - * That is: if the tour re-encounters a node already visited on that + * Each tour explores the subgraph rooted at each node exactly once. + * That is: if the tour re-encounters a node already visited on that * tour, it uses the values already computed for that node. * - * The tourIdHash deliberately depends on the order in which nodes are - * encountered. Therefore, the tourIdHash of a tour depends on the - * entirety of the state of the world and all the world-reachable nodes. + * The tourIdHash deliberately depends on the order in which nodes are + * encountered. Therefore, the tourIdHash of a tour depends on the + * entirety of the state of the world and all the world-reachable nodes. * - * The tourVerSum, being a simple sum, does not depend on the order of + * The tourVerSum, being a simple sum, does not depend on the order of * visiting. */ enum { cVer = 10 -}; +}; typedef struct tourReportStruct { ulongest_t tour; /* tour serial */ ulongest_t tourIdHash; /* hash of node ids, computed last tour */ @@ -128,7 +107,7 @@ static ulongest_t tourSerial = 0; static void tourWorld(tourReport tr_o, mps_addr_t *world, ulongest_t countWorld) { ulongest_t i; - + tourSerial += 1; tr_o->tour = tourSerial; tr_o->tourIdHash = 0; @@ -158,15 +137,15 @@ static void tour_subgraph(tourReport tr_o, struct node_t *node) ulongest_t tourIdHash; Insist(tr_o != NULL); - + /* node == NULL is permitted */ if(node == NULL) return; - + tour = tr_o->tour; if(DYI_INT(node->tour_dyi) == tour) return; /* already visited */ - + /* this is a newly discovered node */ Insist(DYI_INT(node->tour_dyi) < tour); @@ -180,13 +159,13 @@ static void tour_subgraph(tourReport tr_o, struct node_t *node) ver = DYI_INT(node->ver_dyi); Insist(ver < cVer); tr_o->acNodesVer[ver] += 1; - + /* tour the subgraphs (NULL is permitted) */ left = node->left; right = node->right; tour_subgraph(tr_o, left); tour_subgraph(tr_o, right); - + /* computed idHash of subgraph at this node */ id = DYI_INT(node->id_dyi); tourIdHashLeft = left ? DYI_INT(left->tourIdHash_dyi) : 0; @@ -216,12 +195,12 @@ static void after(mps_addr_t *world, ulongest_t countWorld, { longest_t dCVerOld; longest_t dCVerNew; - + tourWorld(&trAfter, world, countWorld); dCVerOld = ((long)trAfter.acNodesVer[verOld] - (long)trBefore.acNodesVer[verOld]); dCVerNew = ((long)trAfter.acNodesVer[verNew] - (long)trBefore.acNodesVer[verNew]); - + progressf(("tourWorld: (%"PRIuLONGEST" %"PRIuLONGEST":%"PRIuLONGEST"/%"PRIuLONGEST":%"PRIuLONGEST") -> (%"PRIuLONGEST" %"PRIuLONGEST":%+"PRIdLONGEST"/%"PRIuLONGEST":%+"PRIdLONGEST"), %s\n", trBefore.tourIdHash, verOld, @@ -253,9 +232,9 @@ static mps_res_t mps_arena_transform_objects_list(mps_bool_t *transform_done_o, mps_res_t res; mps_transform_t transform; mps_bool_t applied = FALSE; - + Insist(old_list_count == new_list_count); - + res = mps_transform_create(&transform, mps_arena); if(res == MPS_RES_OK) { /* We have a transform */ @@ -270,7 +249,7 @@ static mps_res_t mps_arena_transform_objects_list(mps_bool_t *transform_done_o, mps_transform_destroy(transform); } } - + /* Always set *transform_done_o (even if there is also a non-ResOK */ /* return code): it is a status report, not a material return. */ *transform_done_o = applied; @@ -293,10 +272,10 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) { /* Test with sets of pre-built nodes, a known distance apart. * - * This gives control over whether new nodes are on the same + * This gives control over whether new nodes are on the same * segment as the olds or not. */ - + ulongest_t iPerset; ulongest_t aPerset[] = {0, 1, 1, 10, 10, 1000, 1000}; ulongest_t cPerset = NELEMS(aPerset); @@ -317,9 +296,9 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) } progressf(("total: %"PRIuLONGEST".\n", countWorld)); Insist(countWorld <= myrootExactCOUNT); - + keepCount = 0; - + for(iPerset = stepPerset; aPerset[iPerset] != 0; iPerset = (iPerset + stepPerset) % cPerset) { @@ -327,7 +306,7 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) ulongest_t first; ulongest_t skip; ulongest_t count; - + perset = aPerset[iPerset]; first = keepCount; skip = 0; @@ -352,11 +331,11 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) /*printf("Object %"PRIuLONGEST" at %p.\n", keepCount, (void*)v);*/ } v = 0; - + /* >=10? pick subset */ if(perset >= 10) { /* subset of [first..first+perset) */ - + skip = (rnd() % (2 * perset)); if(skip > (perset - 1)) skip = 0; @@ -364,12 +343,12 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) count = 1 + rnd() % (2 * (perset - skip)); if(skip + count > perset) count = perset - skip; - + Insist(skip < perset); Insist(count >= 1); Insist(skip + count <= perset); } - + /* >=10? sometimes build tree */ if(perset >= 10 && count >= 4 && rnd() % 2 == 0) { struct node_t **oldNodes = (struct node_t **)&myrootExact[first + skip]; @@ -382,13 +361,13 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) newNodes[j]->right = newNodes[(2 * j) + 1];} } } - + /* transform {count} olds into {count} news */ before(myrootExact, countWorld); /* after(myrootExact, countWorld, 1, 0, 2, 0); */ progressf(("Transform [%"PRIuLONGEST"..%"PRIuLONGEST") to [%"PRIuLONGEST"..%"PRIuLONGEST").\n", first + skip, first + skip + count, first + skip + perset, first + skip + count + perset)); - res = mps_arena_transform_objects_list(&transform_done, arena, + res = mps_arena_transform_objects_list(&transform_done, arena, &myrootExact[first + skip], count, &myrootExact[first + skip + perset], count); Insist(res == MPS_RES_OK); @@ -397,13 +376,13 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) after(myrootExact, countWorld, 1, -(longest_t)count, 2, 0); } } - + { /* Transforming in various situations * * First, make two sets of 1024 nodes. */ - + perset = 1024; Insist(2*perset < myrootExactCOUNT); for(keepCount = 0; keepCount < 2*perset; keepCount++) { @@ -422,16 +401,16 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) } v = 0; - - /* Functions before() and after() checksum the world, and verify + + /* Functions before() and after() checksum the world, and verify * that the expected transform occurred. */ before(myrootExact, perset); after(myrootExact, perset, 1, 0, 2, 0); - /* Don't transform node 0: its ref coincides with a segbase, so + /* Don't transform node 0: its ref coincides with a segbase, so * there are probably ambiguous refs to it on the stack. - * Don't transform last node either: this test code may leave an + * Don't transform last node either: this test code may leave an * ambiguous reference to it on the stack. */ @@ -482,7 +461,7 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) * * **** USES OBJECTS CREATED IN PREVIOUS TEST GROUP **** */ - + mps_transform_t t1; mps_transform_t t2; mps_bool_t applied = FALSE; @@ -683,10 +662,10 @@ static void showStatsAscii(size_t notcon, size_t con, size_t live, size_t alimit ulongest_t a = cols(alimit); ulongest_t count; ulongest_t i; - + /* if we can show alimit within 200 cols, do so */ count = (a < 200) ? a + 1 : c; - + for(i = 0; i < count; i++) { printf( (i == a) ? "A" : (i < n) ? "n" @@ -701,7 +680,7 @@ static void showStatsAscii(size_t notcon, size_t con, size_t live, size_t alimit /* print_M -- print count of bytes as Mebibytes or Megabytes * - * Print as a whole number, "m" for the decimal point, and + * Print as a whole number, "m" for the decimal point, and * then the decimal fraction. * * Input: 208896 @@ -762,7 +741,7 @@ static void get(mps_arena_t arena) cdie(mps_message_get(&message, arena, type), "get"); - + switch(type) { case mps_message_type_gc_start(): { mclockBegin = mps_message_clock(arena, message); @@ -780,7 +759,7 @@ static void get(mps_arena_t arena) size_t alimit = mps_arena_reserved(arena); mclockEnd = mps_message_clock(arena, message); - + printf(" %5"PRIuLONGEST": (%5"PRIuLONGEST")", mclockEnd, mclockEnd - mclockBegin); printf(" Coll End "); @@ -800,34 +779,34 @@ static void get(mps_arena_t arena) break; } } - + mps_message_discard(arena, message); } } /* .catalog: The Catalog client: - * - * This is an MPS client for testing the MPS. It simulates - * converting a multi-page "Catalog" document from a page-description + * + * This is an MPS client for testing the MPS. It simulates + * converting a multi-page "Catalog" document from a page-description * into a bitmap. * - * The intention is that this task will cause memory usage that is - * fairly realistic (much more so than randomly allocated objects - * with random interconnections. The patterns in common with real + * The intention is that this task will cause memory usage that is + * fairly realistic (much more so than randomly allocated objects + * with random interconnections. The patterns in common with real * clients are: - * - the program input and its task are 'fractal', with a + * - the program input and its task are 'fractal', with a * self-similar hierarchy; - * - object allocation is prompted by each successive element of + * - object allocation is prompted by each successive element of * the input/task; - * - objects are often used to store a transformed version of the + * - objects are often used to store a transformed version of the * program input; * - there may be several stages of transformation; - * - at each stage, the old object (holding the untransformed data) + * - at each stage, the old object (holding the untransformed data) * may become dead; - * - sometimes a tree of objects becomes dead once an object at + * - sometimes a tree of objects becomes dead once an object at * some level of the hierarchy has been fully processed; - * - there is more than one hierarchy, and objects in different + * - there is more than one hierarchy, and objects in different * hierarchies interact. * * The entity-relationship diagram is: @@ -836,12 +815,12 @@ static void get(mps_arena_t arena) * | * Palette --------------------< Colour * - * The first hierarchy is a Catalog, containing Pages, each - * containing Articles (bits of artwork etc), each composed of - * Polygons. Each polygon has a single colour. + * The first hierarchy is a Catalog, containing Pages, each + * containing Articles (bits of artwork etc), each composed of + * Polygons. Each polygon has a single colour. * - * The second hierarchy is a top-level Palette, containing Colours. - * Colours (in this client) are expensive, large objects (perhaps + * The second hierarchy is a top-level Palette, containing Colours. + * Colours (in this client) are expensive, large objects (perhaps * because of complex colour modelling or colour blending). * * The things that matter for their effect on MPS behaviour are: @@ -889,7 +868,7 @@ static void CatalogCheck(void) Page = (void *)w; Insist(DYLAN_VECTOR_SLOT(Page, 0) == DYLAN_INT(PageSig)); Pages += 1; - + for(j = 0; j < PageVar; j += 1) { /* retrieve Art from Page */ w = DYLAN_VECTOR_SLOT(Page, PageFix + j); @@ -917,11 +896,11 @@ static void CatalogCheck(void) /* CatalogDo -- make a Catalog and its tree of objects * - * .catalog.broken: this code, when compiled with - * moderate optimization, may have ambiguous interior pointers but - * lack corresponding ambiguous base pointers to MPS objects. This - * means the interior pointers are unmanaged references, and the - * code goes wrong. The hack in poolamc.c#4 cures this, but not very + * .catalog.broken: this code, when compiled with + * moderate optimization, may have ambiguous interior pointers but + * lack corresponding ambiguous base pointers to MPS objects. This + * means the interior pointers are unmanaged references, and the + * code goes wrong. The hack in poolamc.c#4 cures this, but not very * nicely. For further discussion, see: * */ @@ -934,7 +913,7 @@ static void CatalogDo(mps_arena_t arena, mps_ap_t ap) die(make_dylan_vector(&v, ap, CatalogFix + CatalogVar), "Catalog"); DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(CatalogSig); Catalog = (void *)v; - + /* store Catalog in root */ myrootExact[CatalogRootIndex] = Catalog; get(arena); @@ -950,10 +929,10 @@ static void CatalogDo(mps_arena_t arena, mps_ap_t ap) /* store Page in Catalog */ DYLAN_VECTOR_SLOT(Catalog, CatalogFix + i) = (mps_word_t)Page; get(arena); - + printf("Page %d: make articles\n", i); fflush(stdout); - + for(j = 0; j < PageVar; j += 1) { die(make_dylan_vector(&v, ap, ArtFix + ArtVar), "Art"); DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(ArtSig); @@ -981,8 +960,8 @@ static void CatalogDo(mps_arena_t arena, mps_ap_t ap) /* MakeThing -- make an object of the size requested (in bytes) * - * Any size is accepted. MakeThing may round it up (MakeThing always - * makes a dylan vector, which has a minimum size of 8 bytes). Vector + * Any size is accepted. MakeThing may round it up (MakeThing always + * makes a dylan vector, which has a minimum size of 8 bytes). Vector * slots, if any, are initialized to DYLAN_INT(0). * * After making the object, calls get(), to retrieve MPS messages. @@ -1004,7 +983,7 @@ static void* MakeThing(mps_arena_t arena, mps_ap_t ap, size_t size) slots = words - 2; die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); get(arena); - + return (void *)v; } @@ -1012,7 +991,7 @@ static void BigdropSmall(mps_arena_t arena, mps_ap_t ap, size_t big, char small_ { static ulongest_t keepCount = 0; ulongest_t i; - + mps_arena_park(arena); for(i = 0; i < 100; i++) { (void) MakeThing(arena, ap, big); @@ -1045,7 +1024,7 @@ static void Make(mps_arena_t arena, mps_ap_t ap, unsigned randm, unsigned keep1i { unsigned keepCount = 0; ulongest_t objCount = 0; - + Insist(keepRootspace <= myrootExactCOUNT); objCount = 0; @@ -1102,7 +1081,7 @@ static void Make(mps_arena_t arena, mps_ap_t ap, unsigned randm, unsigned keep1i static void Rootdrop(char rank_char) { ulongest_t i; - + if(rank_char == 'A') { for(i = 0; i < myrootAmbigCOUNT; ++i) { myrootAmbig[i] = NULL; @@ -1122,11 +1101,11 @@ static void stackwipe(void) { unsigned iw; ulongest_t aw[stackwipedepth]; - + /* http://xkcd.com/710/ */ /* I don't want my friends to stop calling; I just want the */ /* compiler to stop optimising away my code. */ - + /* Do you ever get two even numbers next to each other? Hmmmm :-) */ for(iw = 0; iw < stackwipedepth; iw++) { if((iw & 1) == 0) { @@ -1340,7 +1319,7 @@ static void *testscriptB(void *arg, size_t s) "root_create - exact"); die(mps_ap_create(&ap, amc, mps_rank_exact()), "ap_create"); - + /* root_stackreg: stack & registers are ambiguous roots = mutator's workspace */ stack_start = &stack_starts_here; stack_thr = thr; @@ -1409,7 +1388,7 @@ int main(int argc, char *argv[]) { randomize(argc, argv); mps_lib_assert_fail_install(assert_die); - + /* 1<<19 == 524288 == 1/2 Mebibyte */ /* 16<<20 == 16777216 == 16 Mebibyte */ @@ -1425,41 +1404,29 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . - * All rights reserved. This is an open source license. Contact - * Ravenbrook for commercial licensing options. - * + * Copyright (C) 2011-2022 Ravenbrook Limited . + * * 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. - * + * 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. - * + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * * 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. + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR 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/design/transform.txt b/design/transform.txt index 68122ca759..503e8b5cd0 100644 --- a/design/transform.txt +++ b/design/transform.txt @@ -1,46 +1,55 @@ -MPS TRANSFORM DESIGN -==================== -Richard Brooksby, Ravenbrook Limited, 2012-09-04 +.. mode: -*- rst -*- -1. Introduction ---------------- +Transforms +========== + +:Tag: design.mps.transform +:Author: Richard Brooksby +:Date: 2012-09-04 +:Status: complete +:Revision: $Id$ +:Copyright: See `Copyright and License`_. +:Index terms: + pair: transforms; design + -This document describes the Transform mechanism of the Memory Pool -System. Transforms allow the client code to replace a set of object -references on the heap. Transforms exist to satisfy a requirement from -Configura, and are not intended to become part of the open source -MPS. +Introduction +------------ + +This document describes the Transform mechanism of the Memory Pool System. +Transforms allow the client code to replace a set of object references on the +heap. The readership of this document is any developer intending to modify the Transform implementation. -This document is Configura client confidential. - -2. Background -------------- +Background +---------- Göran Rydqvist of Configura originally expressed the requirement for the -MPS to support the change of layout of objects in CET [[Crab -2010-02-25][]]. Ravenbrook proposed several methods [[RHSK -2010-09-21][]] but Configura selected idea 1: - -> Make all objects "direct", without IOHeaders. If you need to add fields, -> then use a special new MPS function (that doesn't exist yet): -> -> mps_arena_transform_objects(&my_transform_function); -> -> This traverses the object graph, lets your transform_function basically -> "realloc()" the field-block, and MPS fixes up all references from other -> objects to point to the new field-block. -> -> Unfortunately, this idea is probably killed off by ambiguous references -> :-(. You could only run the patch if you could *guarantee* there are no -> ambiguous refs you want. In other words, any object refs on the stack -> would become instant death (or worse: subtle slow death :-). Therefore we -> don't really like this idea (unfortunately). There are safer and simpler -> ways to do it, we think... +MPS to support the change of layout of objects in CET [GR_2010-02-25]_. +Ravenbrook proposed several methods [RHSK_2010-09-21]_ including: + + If you need to add fields, then use a special new MPS function (that + doesn't exist yet):: + + mps_arena_transform_objects(&my_transform_function); + + This traverses the object graph, lets your transform_function + basically ``realloc()`` the field-block, and MPS fixes up all + references from other objects to point to the new field-block. + + Unfortunately, this idea is probably killed off by ambiguous + references :-(. You could only run the patch if you could + *guarantee* there are no ambiguous refs you want. In other words, + any object refs on the stack would become instant death (or worse: + subtle slow death :-). Therefore we don't really like this idea + (unfortunately). There are safer and simpler ways to do it, we + think... + +which Configura selected [GR_2010-09-22]_. An initial implementation was made by RHSK and released to Configura as "experimental", however Configura put it into production. @@ -49,78 +58,92 @@ During work on adapting the MPS to 64-bit Windows, RB reformed and reimplemented transforms based on RHSK's original work. -3. Overview ------------ +Overview +-------- The client program builds a table mapping "old" references to "new" ones -in a `Transform` object. This is then "applied", causing a garbage +in a ``Transform`` object. This is then "applied", causing a garbage collection trace in which the fix function is substituted by -`transformFix`, which spots "old" references and replaces them with +``transformFix()``, which spots "old" references and replaces them with "new" ones, in addition to applying the usual garbage collection fix function. -4. NOT YET WRITTEN ------------------- +Not yet written +--------------- -Points to cover: +* Ambiguous references and aborting the transform. -* Ambiguous references and aborting the transform +* How ambiguous references are avoided using ``arena->stackWarm``. - * How ambiguous references are avoided using stackAtEnter +* How the implementation is an add-on to the MPS. -* How the implementation is an add-on to the MPS +* How the code is kept separate from the mainstream MPS. -* How the code is kept separate from the mainstream MPS +* Why it has its own hash table implementation (no good reason). -* Why it has its own hash table implementation (no good reason) +* Why it does a garbage collection and not just a transforming scan. -* Why it does a garbage collection and not just a transforming scan +* Nice side-effect is that "old" objects are killed. - * Nice side-effect is that "old" objects are killed +* Why the arena must be parked. -* Why the arena must be parked +References +---------- -A. References -------------- +.. [GR_2010-02-25] + "Incremental object" (e-mail); + Göran Rydqvist; Configura; 2010-02-25; + . -[Crab 2010-02-25]: http://info.ravenbrook.com/mail/2010/02/25/16-35-45/0/ -[Crab 2010-02-25] "Incremental object" (email); Göran Rydqvist; -Configura; 2010-02-25; -. +.. [RHSK_2010-09-21] + "Incremental object ideas" (e-mail); + Richard Kistruck; Ravenbrook Limited; 2010-09-21; + . -[RHSK 2010-09-21]: http://info.ravenbrook.com/mail/2010/09/21/16-54-59/0/ -[RHSK 2010-09-21] "Incremental object ideas" (email); Richard Kistruck; -Ravenbrook Limited; 2010-09-21; -. +.. [GR_2010-09-22] + "Incremental object ideas" (e-mail); + Göran Rydqvist; Configura; 2010-09-22; + . -[Crab 2010-09-22]: http://info.ravenbrook.com/mail/2010/09/22/09-27-53/0/ -[Crab 2010-09-22] "Incremental object ideas" (email); Göran Rydqvist; -Configura; 2010-09-22; -. +Document History +---------------- -B. Document History -------------------- +- 2012-09-04 RB_ First draft. -* 2012-09-04 [RB](mailto:rb@ravenbrook.com) First draft. +- 2022-01-23 GDR_ Converted to reStructuredText. +.. _RB: https://www.ravenbrook.com/consultants/rb/ +.. _GDR: https://www.ravenbrook.com/consultants/gdr/ -C. Copyright and Licence ------------------------- -This document is copyright © 2012 [Ravenbrook Limited](http://www.ravenbrook.com). All rights reserved. This is an -open source license. Contact Ravenbrook for commercial licensing -options. +Copyright and License +--------------------- -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Copyright © 2012–2020 `Ravenbrook Limited `_. -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -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. +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. -3. Redistributions in any form must be accompanied by information on how to obtain complete source code for the 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. +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. -**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.** +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 AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR 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/manual/source/glossary/t.rst b/manual/source/glossary/t.rst index 4d094735d7..879f7a8a54 100644 --- a/manual/source/glossary/t.rst +++ b/manual/source/glossary/t.rst @@ -233,6 +233,15 @@ Memory Management Glossary: T objects are reachable. Those that were not reachable may be :term:`reclaimed`. + transform + + .. mps:specifc:: + + A mapping from old :term:`references` to new references, + represented by :c:type:`mps_transform_t`, that can be applied + to all references managed by the MPS. See + :ref:`topic-transform`. + translation buffer translation lookaside buffer diff --git a/manual/source/release.rst b/manual/source/release.rst index 6ff3db2958..3c01887016 100644 --- a/manual/source/release.rst +++ b/manual/source/release.rst @@ -44,6 +44,11 @@ New features :ref:`topic-scanning-protocol`. This allows the client program to safely update references in the visited objects. +#. The new **transforms** feature updates references throughout the + automatically managed portion of the heap. For some use cases this + may be more convenient than :c:func:`mps_pool_walk`. See + :ref:`topic-transform`. + Interface changes ................. diff --git a/manual/source/topic/index.rst b/manual/source/topic/index.rst index b3139c16fb..c592c98d21 100644 --- a/manual/source/topic/index.rst +++ b/manual/source/topic/index.rst @@ -26,6 +26,7 @@ Reference debugging telemetry weak + transform plinth platform porting diff --git a/manual/source/custom/cet/transform.rst b/manual/source/topic/transform.rst similarity index 56% rename from manual/source/custom/cet/transform.rst rename to manual/source/topic/transform.rst index aa9c27a6d8..75ea1a441d 100644 --- a/manual/source/custom/cet/transform.rst +++ b/manual/source/topic/transform.rst @@ -1,22 +1,26 @@ -.. Sources: - - ``_ - .. index:: single: transform; introduction +.. _topic-transform: + Transforms ========== In a long-running interactive system, it may be desirable to change the format of live objects. In some programming languages (notably Smalltalk), when the programmer edits a class definition, objects belonging to the class must be updated so that they are valid instances of the redefined class. This may involve adding or removing fields from each instance and so changing the size of the allocated objects. -If the object has grown as a result of the redefinition, this redefinition can't be done in-place, so what actually happens is that for each instance of the old version of the class, a corresponding instance of the new version of the class is created, and all references to the old instance are rewritten to refer to the new instance. And discovering "all references" to an object is a task that falls to the garbage collector. +If the object has grown as a result of the redefinition, this +redefinition can't be done in-place, so what actually happens is that +for each instance of the old version of the class, a corresponding +instance of the new version of the class is created, and all +:term:`references` to the old instance are rewritten to refer to the new +instance. Discovering "all references" to an object is a task that falls +to the garbage collector. *Transforms* are a general mechanism by which the client program requests the MPS to replace references to one set of objects (the *old* objects) with references to another (the *new* objects). The MPS performs this task by carrying out a complete garbage collection, in the course of which all references to old objects are discovered and substituted with references to the corresponding new object. -Transform cautions ------------------- +Cautions +-------- 1. The arena must be :term:`parked ` (for example, by calling :c:func:`mps_arena_park`) before creating the transform and @@ -28,32 +32,45 @@ Transform cautions whether or not the reference should be updated to point to the new object.) +.. warning:: + + The second caution means that transforms may be unsuitable for + client programs that treat the :term:`registers` and :term:`control + stack` as a :term:`root`, by using :c:func:`mps_root_create_thread` + and similar functions, unless the program can guarantee that none of + the old references will be referenced by this root. + + An alternative and more robust approach is to segregate the + :term:`formatted objects` that need to be updated into a suitable + :term:`pool`, and iterate over them using the function + :c:func:`mps_pool_walk`. + .. index:: single: transform; interface -Transform interface -------------------- +Interface +--------- :: - #include "mpstr.h" + #include "mps.h" .. c:type:: mps_transform_t - The type of transforms. A transform represents a mapping from - *old* objects to *new* objects. + The type of :term:`transforms`. A transform represents a mapping from *old* + :term:`references` to *new* references. .. c:function:: mps_res_t mps_transform_create(mps_transform_t *transform_o, mps_arena_t arena) - Create an empty transform. + Create an empty :term:`transform`. ``transform_o`` points to a location that will hold the address of the new transform. - ``arena`` is the arena in which to create the transform. + ``arena`` is the :term:`arena` in which to create the transform. :c:func:`mps_transform_create` returns :c:macro:`MPS_RES_OK` if successful. The MPS may exhaust some resource in the course of @@ -71,15 +88,16 @@ Transform interface .. c:function:: mps_res_t mps_transform_add_oldnew(mps_transform_t transform, mps_addr_t *old_array, mps_addr_t *new_array, size_t count) - Add mappings from old to new objects to a transform. + Add mappings from an old :term:`reference` to a new reference to a + :term:`transform`. ``transform`` is the transform to which the mappings will be added. - ``old_array`` points to an array of references to old objects. + ``old_array`` points to an array of old references. - ``new_array`` points to an array of references to new objects. + ``new_array`` points to an array of corresponding new references. - ``count`` is the number of references in each array. + ``count`` is the number of references in both arrays. :c:func:`mps_transform_add_oldnew` returns :c:macro:`MPS_RES_OK` if successful. The MPS may exhaust some resource in the course of @@ -88,45 +106,46 @@ Transform interface .. note:: - An old object must be added at most once to a transform. + Each old reference must be added at most once to a given + transform. .. c:function:: mps_res_t mps_transform_apply(mps_bool_t *applied_o, mps_transform_t transform) - Attempt to apply a transform. + Attempt to apply a :term:`transform`. ``applied_o`` points to a location that will hold a Boolean indicating whether or not the transform was applied. ``transform`` is the transform to apply. - If the arena is currently incapable of applying the transform, - then an appropriate :term:`result code` is returned, and the - location pointed to by ``applied_o`` is not updated. Possible + If the :term:`arena` is currently incapable of applying the + transform, then an appropriate :term:`result code` is returned, and + the location pointed to by ``applied_o`` is not updated. Possible causes include (but are not limited to) the arena not being in the :term:`parked state` (in which case the result code is - :c:macro:`MPS_RES_LIMIT`), or a collection having taken place - since ``transform`` was created (in which case the result code is + :c:macro:`MPS_RES_LIMIT`), or a collection having taken place since + ``transform`` was created (in which case the result code is :c:macro:`MPS_RES_PARAM`). If the arena is *capable* of applying the transform, then the MPS - carries out a garbage collection, the arena is left in the + carries out a :term:`garbage collection`, the arena is left in the :term:`parked state`, :c:func:`mps_transform_apply` returns - :c:macro:`MPS_RES_OK`, and the location pointed to by - ``applied_o`` is updated. + :c:macro:`MPS_RES_OK`, and the location pointed to by ``applied_o`` + is updated. - If in the course of the ambiguous reference was discovered, then - the transform is aborted and ``*applied_o`` is set to false. In - this case, *no* references to the old objects are updated. (That - is, either *all* of the transform is applied, or *none* of it.) + If in the course of the application, an :term:`ambiguous reference` + was discovered, then the transform is aborted and ``*applied_o`` is + set to false. In this case, *no* references to the old objects are + updated. (That is, either *all* of the transform is applied, or + *none* of it.) - If the transform was successfully applied, it is destroyed (as if - :c:func:`mps_transform_destroy` had been called). + If the transform was successfully applied, it is destroyed, as if + :c:func:`mps_transform_destroy` had been called. .. c:function:: void mps_transform_destroy(mps_transform_t transform) - Destroy a transform. + Destroy a :term:`transform`. ``transform`` is the transform to destroy. - From b928fa236178fb1bdbe20442c3f53b8e8a545a4b Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 28 Jan 2022 18:55:23 +0000 Subject: [PATCH 3/3] Make hash arrays and extension callbacks part of the public MPS. * Move type and macro declarations to the public header mps.h. * Move documentation to appropriate sections of manual. --- code/mps.h | 17 ++++ code/mpscvm.h | 79 ------------------ manual/source/custom/cet/extension.rst | 108 ------------------------- manual/source/custom/cet/hasharray.rst | 61 -------------- manual/source/custom/cet/index.rst | 11 --- manual/source/index.rst | 1 - manual/source/pool/amc.rst | 62 ++++++++++++++ manual/source/release.rst | 12 +++ manual/source/topic/arena.rst | 93 ++++++++++++++++++++- 9 files changed, 181 insertions(+), 263 deletions(-) delete mode 100644 code/mpscvm.h delete mode 100644 manual/source/custom/cet/extension.rst delete mode 100644 manual/source/custom/cet/hasharray.rst delete mode 100644 manual/source/custom/cet/index.rst diff --git a/code/mps.h b/code/mps.h index 2514236351..0793224924 100644 --- a/code/mps.h +++ b/code/mps.h @@ -120,6 +120,13 @@ typedef mps_addr_t (*mps_fmt_isfwd_t)(mps_addr_t); typedef void (*mps_fmt_pad_t)(mps_addr_t, size_t); typedef mps_addr_t (*mps_fmt_class_t)(mps_addr_t); +/* Callbacks indicating that the arena has extended or contracted. + * These are used to register chunks with RtlInstallFunctionTableCallback + * + * so that the client can unwind the stack through functions in the arena. + */ +typedef void (*mps_arena_extended_t)(mps_arena_t, mps_addr_t, size_t); +typedef void (*mps_arena_contracted_t)(mps_arena_t, mps_addr_t, size_t); /* Keyword argument lists */ @@ -171,6 +178,12 @@ extern const struct mps_key_s _mps_key_ARENA_SIZE; extern const struct mps_key_s _mps_key_ARENA_ZONED; #define MPS_KEY_ARENA_ZONED (&_mps_key_ARENA_ZONED) #define MPS_KEY_ARENA_ZONED_FIELD b +extern const struct mps_key_s _mps_key_arena_extended; +#define MPS_KEY_ARENA_EXTENDED (&_mps_key_arena_extended) +#define MPS_KEY_ARENA_EXTENDED_FIELD fun +extern const struct mps_key_s _mps_key_arena_contracted; +#define MPS_KEY_ARENA_CONTRACTED (&_mps_key_arena_contracted) +#define MPS_KEY_ARENA_CONTRACTED_FIELD fun extern const struct mps_key_s _mps_key_FORMAT; #define MPS_KEY_FORMAT (&_mps_key_FORMAT) #define MPS_KEY_FORMAT_FIELD format @@ -247,6 +260,10 @@ extern const struct mps_key_s _mps_key_FMT_CLASS; #define MPS_KEY_FMT_CLASS (&_mps_key_FMT_CLASS) #define MPS_KEY_FMT_CLASS_FIELD fmt_class +extern const struct mps_key_s _mps_key_ap_hash_arrays; +#define MPS_KEY_AP_HASH_ARRAYS (&_mps_key_ap_hash_arrays) +#define MPS_KEY_AP_HASH_ARRAYS_FIELD b + /* Maximum length of a keyword argument list. */ #define MPS_ARGS_MAX 32 diff --git a/code/mpscvm.h b/code/mpscvm.h deleted file mode 100644 index 886f8e5a3f..0000000000 --- a/code/mpscvm.h +++ /dev/null @@ -1,79 +0,0 @@ -/* mpscvm.h: RAVENBROOK MEMORY POOL SYSTEM C INTERFACE TO CONFIGURA VIRTUAL MACHINE - * - * $Id$ - * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. - * - * This file defines interfaces to miscellaneous extensions to the MPS - * for interfacing to the Configura Virtual Machine. It is not part - * of the open source MPS. - * - * .readership: CVM developers, MPS developers. - */ - -#ifndef mpscvm_h -#define mpscvm_h - -#include "mps.h" - -/* Callbacks indicating that the arena has extended or contracted. - These are used to register chunks with RtlInstallFunctionTableCallback - - so that CVM can unwind the stack through functions in the arena. */ - -typedef void (*mps_arena_extended_t)(mps_arena_t, mps_addr_t, size_t); -typedef void (*mps_arena_contracted_t)(mps_arena_t, mps_addr_t, size_t); - -extern const struct mps_key_s _mps_key_arena_extended; -#define MPS_KEY_ARENA_EXTENDED (&_mps_key_arena_extended) -#define MPS_KEY_ARENA_EXTENDED_FIELD fun - -extern const struct mps_key_s _mps_key_arena_contracted; -#define MPS_KEY_ARENA_CONTRACTED (&_mps_key_arena_contracted) -#define MPS_KEY_ARENA_CONTRACTED_FIELD fun - -extern const struct mps_key_s _mps_key_ap_hash_arrays; -#define MPS_KEY_AP_HASH_ARRAYS (&_mps_key_ap_hash_arrays) -#define MPS_KEY_AP_HASH_ARRAYS_FIELD b - -#endif /* mpscvm_h */ - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2011 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/manual/source/custom/cet/extension.rst b/manual/source/custom/cet/extension.rst deleted file mode 100644 index 5c13503e68..0000000000 --- a/manual/source/custom/cet/extension.rst +++ /dev/null @@ -1,108 +0,0 @@ -.. index:: - single: arena extension callbacks; introduction - single: extension callbacks; introduction - single: arena contraction callbacks; introduction - single: contraction callbacks; introduction - -.. _topic-extension: - -Arena extension callbacks -========================= - -There are situations in which the :term:`client program` needs to be -informed about the chunks of address space that an :term:`arena` is -managing. To support this, the MPS allows the client program to -specify two callback functions when creating a :term:`virtual memory -arena`: one function is called when the arena is *extended* (that is, -when it acquires a new chunk of address space from the operating -system), and the other when the arena is *contracted* (that is, when -it returns a chunk of address space to the operating system). - -The use case that this feature is designed to support is debugging of dynamically generated code in 64-bit Windows. `Microsoft explains `_: - -.. _`MSDN`: http://msdn.microsoft.com/en-us/library/windows/desktop/ms680595.aspx - - Function tables are used on 64-bit Windows to determine how to - unwind or walk the stack. These tables are usually generated by - the compiler and stored as part of the image. However, - applications must provide the function table for dynamically - generated code. - -An application may install a dynamic function table by calling ``RtlInstallFunctionTableCallback()``, passing the region of memory in which the dynamically generated functions can be found, and may later delete the table by calling ``RtlDeleteFunctionTable()``. - -So if the client program is storing dynamically generated functions in MPS-managed memory, then it could define callback functionss that install and delete the function table callback for the dynamically generated code, like this:: - - void arena_extended(mps_arena_t arena, void *base, size_t size) - { - RtlInstallFunctionTableCallback(...); - } - - void arena_contracted(mps_arena_t arena, void *base, size_t size) - { - RtlDeleteFunctionTable(...); - } - -and then pass these two functions using :term:`keyword arguments` to :c:func:`mps_arena_create_k`:: - - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_ARENA_EXTENDED, (mps_fun_t)arena_extended); - MPS_ARGS_ADD(args, MPS_KEY_ARENA_CONTRACTED, (mps_fun_t)arena_contracted); - /* ... other keyword arguments ... */ - MPS_ARGS_DONE(args); - res = mps_arena_create_k(&arena, mps_arena_class_vm(), args); - } MPS_ARGS_END(args); - - -.. index:: - single: arena extension callbacks; interface - single: extension callbacks; interface - single: arena contraction callbacks; interface - single: contraction callbacks; interface - -Arena extension callback interface ----------------------------------- - -:: - - #include "mpscvm.h" - -.. c:macro:: MPS_KEY_ARENA_EXTENDED - - A :term:`keyword argument` (of type :c:type:`mps_fun_t`) to - :c:func:`mps_arena_create_k` whose value specifies a function that - will be called when the arena is *extended*: that is, when it - acquires a new chunk of address space from the operating system. - - The function receives three arguments: ``arena`` (the arena being - extended), ``base`` (the base address of the new chunk of address - space), and ``size`` (the size of the new chunk of address space, - in bytes). - - The function must not call any function in the MPS, and must not - access any memory managed by the MPS. - - .. note:: - - This keyword argument is only supported by :term:`virtual - memory arenas`. - -.. c:macro:: MPS_KEY_ARENA_CONTRACTED - - A :term:`keyword argument` (of type :c:type:`mps_fun_t`) to - :c:func:`mps_arena_create_k` whose value specifies a function that - will be called when the arena is *contracted*: that is, when it - finishes with a chunk of address space and returns it to the - operating system. - - The function receives three arguments: ``arena`` (the arena being - contracted), ``base`` (the base address of the old chunk of - address space), and ``size`` (the size of the old chunk of address - space, in bytes). - - The function must not call any function in the MPS, and must not - access any memory managed by the MPS. - - .. note:: - - This keyword argument is only supported by :term:`virtual - memory arenas`. diff --git a/manual/source/custom/cet/hasharray.rst b/manual/source/custom/cet/hasharray.rst deleted file mode 100644 index 06097cb7f8..0000000000 --- a/manual/source/custom/cet/hasharray.rst +++ /dev/null @@ -1,61 +0,0 @@ - -.. index:: - single: hash array; introduction - -Hash arrays -=========== - -The :term:`location dependency` feature of the MPS allows the -:term:`client program` to implement address-based hash tables in the -presence of a :term:`moving memory manager`, re-hashing the tables -when the addresses they contain might have moved. - -However, when a frequently-used hash table grows large enough, the following sequence of events may take place: - -1. The hash table discovers that its location dependency is stale. - -2. A new array is allocated to contain the re-hashed keys. - -3. The new array is large enough to push the *new size* of the - :term:`nursery space` (that is, the amount of newly allocated - memory since the last collection in the first :term:`generation` in - the :term:`generation chain` for the pool containing the array) - close to its capacity. - -4. A small amount of additional allocation causes the new size of the - nursery generation to exceed its capacity, which causes the MPS to - start a new collection of that generation. This in turn causes the - hash table to become stale again. - -When the hash table reaches this critical size, the client program may find that a large fraction of its time is being spent re-hashing the table. - -In order to avoid this happening, the MPS provides a mechanism allowing you specify that the newly allocated array does not contibute to the new size of the nursery space: this cuts off the vicious cycle at step 3. - -See :ref:`topic-collection-schedule` for an explanation of the *new -size* of a generation, and how the MPS uses this to determine when to -start a collection of that generation. - - -.. index:: - single: hash array; interface - -Hash array interface --------------------- - -:: - - #include "mpscvm.h" - -.. c:macro:: MPS_KEY_AP_HASH_ARRAYS - - A :term:`keyword argument` (of type :c:type:`mps_bool_t`, - defaulting to false) to :c:func:`mps_ap_create_k`. If true, it - specifies that blocks allocated from the allocation point do not - contribute to the *new size* of the :term:`nursery space` for the - purposes of deciding whether to start a collection of that - generation. - - .. note:: - - This keyword argument is only supported by :ref:`pool-amc` - pools. diff --git a/manual/source/custom/cet/index.rst b/manual/source/custom/cet/index.rst deleted file mode 100644 index f274a6cd2b..0000000000 --- a/manual/source/custom/cet/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _custom_cet: - -Custom features for CET -*********************** - -.. toctree:: - :numbered: - - transform - extension - hasharray diff --git a/manual/source/index.rst b/manual/source/index.rst index b70fe2627f..5d61d583c2 100644 --- a/manual/source/index.rst +++ b/manual/source/index.rst @@ -7,7 +7,6 @@ Memory Pool System guide/index topic/index pool/index - custom/cet/index design/index design/old diff --git a/manual/source/pool/amc.rst b/manual/source/pool/amc.rst index 4760f928bd..3e4009d7ee 100644 --- a/manual/source/pool/amc.rst +++ b/manual/source/pool/amc.rst @@ -139,3 +139,65 @@ AMC interface MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt); res = mps_pool_create_k(&pool, arena, mps_class_amc(), args); } MPS_ARGS_END(args); + + When creating an :term:`allocation point` on an AMC pool, + :c:func:`mps_ap_create_k` accepts one optional keyword argument: + + * :c:macro:`MPS_KEY_AP_HASH_ARRAYS` (type :c:type:`mps_bool_t`, + defaulting to false) specifies (if true) that blocks allocated + from the allocation point do not contribute to the *new size* of + the :term:`nursery space` for the purposes of deciding whether + to start a collection of that generation. See + :ref:`pool-amc-hash-arrays`. + + +.. index:: + pair: AMC pool class; hash arrays + +.. _pool-amc-hash-arrays: + +Hash arrays +----------- + +The :term:`location dependency` feature of the MPS allows the +:term:`client program` to implement address-based hash tables in pools +like AMC that use a :term:`moving memory manager`, re-hashing the +tables when the addresses they contain might have moved. + +However, when a frequently-used hash table grows large enough, the +following sequence of events may take place: + +1. The hash table discovers that its location dependency is stale. + +2. A new array is allocated to contain the re-hashed keys. + +3. The new array is large enough to push the *new size* of the + :term:`nursery space` (that is, the amount of newly allocated + memory since the last collection in the first :term:`generation` in + the :term:`generation chain` for the pool containing the array) + close to its capacity. + +4. A small amount of additional allocation causes the new size of the + nursery generation to exceed its capacity, which causes the MPS to + start a new collection of that generation. This in turn causes the + hash table to become stale again. + +When the hash table reaches this critical size, the client program may +find that a large fraction of its time is being spent re-hashing the +table. + +In order to avoid this happening, the MPS provides a mechanism for +specifying that the newly allocated array does not contibute to the +new size of the nursery space: this cuts off the vicious cycle at step +3. To use this mechanism, pass true for the +:c:macro:`MPS_KEY_AP_HASH_ARRAYS` keyword argument to +:c:func:`mps_ap_create_k`. For example:: + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_AP_HASH_ARRAYS, TRUE); + res = mps_ap_create_k(&ap, pool, args); + } MPS_ARGS_END(args); + +See :ref:`topic-collection-schedule` for an explanation of the *new +size* of a generation, and how the MPS uses this to determine when to +start a collection of that generation. diff --git a/manual/source/release.rst b/manual/source/release.rst index 3c01887016..653f886c0a 100644 --- a/manual/source/release.rst +++ b/manual/source/release.rst @@ -49,6 +49,18 @@ New features may be more convenient than :c:func:`mps_pool_walk`. See :ref:`topic-transform`. +#. A :term:`virtual memory arena` can now be configured to call + functions when it acquires a new chunk of :term:`address space`, + and when it returns a chunk of address space to the operation + system. This is intended to support dynamic function tables in + Windows. See :ref:`topic-arena-extension`. + +#. An :term:`allocation point` for a pool belonging to the class + :ref:`pool-amc` can now be configured so that allocations do not + provoke garbage collections, reducing the amount of re-hashing for + address-based hash tables using :term:`location dependency`. See + :ref:`pool-amc-hash-arrays`. + Interface changes ................. diff --git a/manual/source/topic/arena.rst b/manual/source/topic/arena.rst index 0e6e086e9b..4b48e03671 100644 --- a/manual/source/topic/arena.rst +++ b/manual/source/topic/arena.rst @@ -139,7 +139,7 @@ Client arenas * :c:macro:`MPS_KEY_ARENA_SIZE` (type :c:type:`size_t`) is its size. - It also accepts three optional keyword arguments: + It also accepts five optional keyword arguments: * :c:macro:`MPS_KEY_COMMIT_LIMIT` (type :c:type:`size_t`) is the maximum amount of memory, in :term:`bytes (1)`, that the MPS @@ -159,6 +159,17 @@ Client arenas arena may pause the :term:`client program` for. See :c:func:`mps_arena_pause_time_set` for details. + * :c:macro:`MPS_KEY_ARENA_EXTENDED` (type :c:type:`mps_fun_t`) is + a function that will be called when the arena is *extended*: + that is, when it acquires a new chunk of address space from the + operating system. See :ref:`topic-arena-extension` for details. + + * :c:macro:`MPS_KEY_ARENA_CONTRACTED` (type :c:type:`mps_fun_t`) + is a function that will be called when the arena is + *contracted*: that is, when it finishes with a chunk of address + space and returns it to the operating system. See + :ref:`topic-arena-extension` for details. + For example:: MPS_ARGS_BEGIN(args) { @@ -983,8 +994,8 @@ Arena introspection and debugging from MPS-managed memory, then it may attempt to re-enter the MPS, which will fail as the MPS is not re-entrant. - .. |RtlInstallFunctionTableCallback| replace:: ``RtlInstallFunctionTableCallback()`` - .. _RtlInstallFunctionTableCallback: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680595(v=vs.85).aspx + .. |RtlInstallFunctionTableCallback| replace:: :c:func:`RtlInstallFunctionTableCallback` + .. _RtlInstallFunctionTableCallback: https://docs.microsoft.com/en-gb/windows/win32/api/winnt/nf-winnt-rtlinstallfunctiontablecallback If this happens, in order to allow the debugger to finish decoding the call stack, the only remedy is to put the arena @@ -1040,3 +1051,79 @@ Arena introspection and debugging :c:func:`mps_addr_pool`, and to find out which :term:`object format` describes the object at the address, use :c:func:`mps_addr_fmt`. + + +.. index:: + single: arena extension callbacks; introduction + single: extension callbacks; introduction + single: arena contraction callbacks; introduction + single: contraction callbacks; introduction + +.. _topic-arena-extension: + +Arena extension callbacks +------------------------- + +There are situations in which the :term:`client program` needs to be +informed about the chunks of address space that an :term:`arena` is +managing. To support this, the MPS allows the client program to +specify two callback functions when creating a :term:`virtual memory +arena`: one function is called when the arena is *extended* (that is, +when it acquires a new chunk of address space from the operating +system), and the other when the arena is *contracted* (that is, when +it returns a chunk of address space to the operating system). + +The use case that this feature is designed to support is debugging of +dynamically generated code in 64-bit Windows. Microsoft's +documentation for |RtlInstallFunctionTableCallback|_ says: + + Function tables are used on 64-bit Windows to determine how to + unwind or walk the stack. These tables are usually generated by + the compiler and stored as part of the image. However, + applications must provide the function table for dynamically + generated code. + +An application may install a dynamic function table by calling +|RtlInstallFunctionTableCallback|_, passing the region of memory in +which the dynamically generated functions can be found, and may later +delete the table by calling |RtlDeleteFunctionTable|_. + +.. |RtlDeleteFunctionTable| replace:: :c:func:`RtlDeleteFunctionTable` +.. _RtlDeleteFunctionTable: https://docs.microsoft.com/en-gb/windows/win32/api/winnt/nf-winnt-rtldeletefunctiontable + +So if the client program is storing dynamically generated functions in +MPS-managed memory, then it could define callback functions that +install and delete the function table callback for the dynamically +generated code, like this:: + + void arena_extended(mps_arena_t arena, void *base, size_t size) + { + RtlInstallFunctionTableCallback(...); + } + + void arena_contracted(mps_arena_t arena, void *base, size_t size) + { + RtlDeleteFunctionTable(...); + } + +and then pass these two functions using :term:`keyword arguments` to +:c:func:`mps_arena_create_k`:: + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_EXTENDED, (mps_fun_t)arena_extended); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_CONTRACTED, (mps_fun_t)arena_contracted); + /* ... other keyword arguments ... */ + res = mps_arena_create_k(&arena, mps_arena_class_vm(), args); + } MPS_ARGS_END(args); + +The callback functions receive three arguments: ``arena`` (the arena +being extended or contracted), ``base`` (the base address of the chunk +of address space that has just been acquired from, or is about to be +returned to, the operating system), and ``size`` (the size of the +chunk, in bytes). They must not call any function in the MPS, and must +not access any memory managed by the MPS. + +.. note:: + + Arena extension callbacks are only supported by :term:`virtual + memory arenas`.