diff --git a/mps/code/commpost.nmk b/mps/code/commpost.nmk index 793ec8e6035..942caf26a01 100644 --- a/mps/code/commpost.nmk +++ b/mps/code/commpost.nmk @@ -1,44 +1,21 @@ -# commpost.nmk: SECOND COMMON FRAGMENT FOR PLATFORMS USING MV AND NMAKE +# commpost.nmk: SECOND COMMON FRAGMENT FOR PLATFORMS USING NMAKE -*- makefile -*- # # $Id$ -# Copyright (c) 2001 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. # # DESCRIPTION # # Second common makefile fragment for w3*mv.nmk. See commpre.nmk -# PSEUDO-TARGETS +# == Pseudo-targets == -# "all" builds all the varieties of all targets -# %%TARGET: When adding a new target, add it to the all dependencies. - -all: mpmss.exe amcss.exe amsss.exe amssshe.exe segsmss.exe awlut.exe awluthe.exe\ - mpsicv.exe lockutw3.exe lockcov.exe poolncv.exe locv.exe qs.exe apss.exe \ - sacss.exe finalcv.exe finaltest.exe \ - arenacv.exe bttest.exe teletest.exe \ - abqtest.exe cbstest.exe btcv.exe mv2test.exe messtest.exe steptest.exe \ - locbwcss.exe locusss.exe zcoll.exe zmess.exe \ - mpseventcnv.exe mpseventtxt.exe \ - mps.lib +all: $(ALL_TARGETS) # Convenience targets -# %%TARGET: When adding a new target, add a pseudo-target for it here, -# first rule for variety-dependent targets, and second for -# variety-independent ones. - -mpmss.exe amcss.exe amcsshe.exe amsss.exe amssshe.exe segsmss.exe awlut.exe awluthe.exe dwstress.exe \ - mpsicv.exe lockutw3.exe lockcov.exe poolncv.exe locv.exe qs.exe apss.exe \ - sacss.exe finalcv.exe finaltest.exe \ - arenacv.exe bttest.exe teletest.exe \ - expt825.exe \ - abqtest.exe cbstest.exe btcv.exe mv2test.exe messtest.exe steptest.exe \ - walkt0.exe locbwcss.exe locusss.exe \ - exposet0.exe zcoll.exe zmess.exe \ - replay.exe replaysw.exe mpseventcnv.exe mpseventtxt.exe mpseventsql.exe \ - mps.lib: +$(ALL_TARGETS): !IFDEF VARIETY $(MAKE) /nologo /f $(PFM).nmk TARGET=$@ variety !ELSE @@ -79,6 +56,17 @@ variety: $(PFM)\$(VARIETY)\$(TARGET) mpsicv.cov: $(MAKE) /nologo /f $(PFM).nmk TARGET=$@ VARIETY=cv variety +# testrun +# Runs automated test cases. + +testrun: $(AUTO_TEST_TARGETS) +!IFDEF VARIETY + ..\tool\testrun.bat $(PFM) $(VARIETY) $(AUTO_TEST_TARGETS) +!ELSE + $(MAKE) /nologo /f $(PFM).nmk VARIETY=hot testrun + $(MAKE) /nologo /f $(PFM).nmk VARIETY=cool testrun +!ENDIF + # THE MPS LIBRARY # @@ -133,41 +121,8 @@ $(PFM)\di\mps.lib: \ !IFDEF VARIETY -$(PFM)\$(VARIETY)\finalcv.exe: $(PFM)\$(VARIETY)\finalcv.obj \ - $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\finaltest.exe: $(PFM)\$(VARIETY)\finaltest.obj \ - $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\expt825.exe: $(PFM)\$(VARIETY)\expt825.obj \ - $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\locv.exe: $(PFM)\$(VARIETY)\locv.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\mpmss.exe: $(PFM)\$(VARIETY)\mpmss.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\apss.exe: $(PFM)\$(VARIETY)\apss.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\sacss.exe: $(PFM)\$(VARIETY)\sacss.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\bttest.exe: $(PFM)\$(VARIETY)\bttest.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\teletest.exe: $(PFM)\$(VARIETY)\teletest.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\lockcov.exe: $(PFM)\$(VARIETY)\lockcov.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\lockutw3.exe: $(PFM)\$(VARIETY)\lockutw3.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\mpsicv.exe: $(PFM)\$(VARIETY)\mpsicv.obj \ - $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\abqtest.exe: $(PFM)\$(VARIETY)\abqtest.obj \ + $(PFM)\$(VARIETY)\abq.obj $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(PFM)\$(VARIETY)\amcss.exe: $(PFM)\$(VARIETY)\amcss.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) @@ -175,24 +130,21 @@ $(PFM)\$(VARIETY)\amcss.exe: $(PFM)\$(VARIETY)\amcss.obj \ $(PFM)\$(VARIETY)\amcsshe.exe: $(PFM)\$(VARIETY)\amcsshe.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\amcssth.exe: $(PFM)\$(VARIETY)\amcssth.obj \ + $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) + $(PFM)\$(VARIETY)\amsss.exe: $(PFM)\$(VARIETY)\amsss.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) $(PFM)\$(VARIETY)\amssshe.exe: $(PFM)\$(VARIETY)\amssshe.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\segsmss.exe: $(PFM)\$(VARIETY)\segsmss.obj \ - $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\locbwcss.exe: $(PFM)\$(VARIETY)\locbwcss.obj \ +$(PFM)\$(VARIETY)\apss.exe: $(PFM)\$(VARIETY)\apss.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\locusss.exe: $(PFM)\$(VARIETY)\locusss.obj \ +$(PFM)\$(VARIETY)\arenacv.exe: $(PFM)\$(VARIETY)\arenacv.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\dwstress.exe: $(PFM)\$(VARIETY)\dwstress.obj \ - $(DWOBJ) $(PFM)\$(VARIETY)\mps.lib - $(PFM)\$(VARIETY)\awlut.exe: $(PFM)\$(VARIETY)\awlut.obj \ $(FMTTESTOBJ) \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) @@ -201,27 +153,75 @@ $(PFM)\$(VARIETY)\awluthe.exe: $(PFM)\$(VARIETY)\awluthe.obj \ $(FMTTESTOBJ) \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\btcv.exe: $(PFM)\$(VARIETY)\btcv.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\bttest.exe: $(PFM)\$(VARIETY)\bttest.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\cbstest.exe: $(PFM)\$(VARIETY)\cbstest.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\exposet0.exe: $(PFM)\$(VARIETY)\exposet0.obj \ + $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\expt825.exe: $(PFM)\$(VARIETY)\expt825.obj \ + $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\finalcv.exe: $(PFM)\$(VARIETY)\finalcv.obj \ + $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\finaltest.exe: $(PFM)\$(VARIETY)\finaltest.obj \ + $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\locbwcss.exe: $(PFM)\$(VARIETY)\locbwcss.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\lockcov.exe: $(PFM)\$(VARIETY)\lockcov.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\lockutw3.exe: $(PFM)\$(VARIETY)\lockutw3.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\locusss.exe: $(PFM)\$(VARIETY)\locusss.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\locv.exe: $(PFM)\$(VARIETY)\locv.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\messtest.exe: $(PFM)\$(VARIETY)\messtest.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\mpmss.exe: $(PFM)\$(VARIETY)\mpmss.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\mpsicv.exe: $(PFM)\$(VARIETY)\mpsicv.obj \ + $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\mv2test.exe: $(PFM)\$(VARIETY)\mv2test.obj \ + $(PFM)\$(VARIETY)\poolmv2.obj $(PFM)\$(VARIETY)\abq.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + $(PFM)\$(VARIETY)\poolncv.exe: $(PFM)\$(VARIETY)\poolncv.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(PFM)\$(VARIETY)\qs.exe: $(PFM)\$(VARIETY)\qs.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\arenacv.exe: $(PFM)\$(VARIETY)\arenacv.obj \ +$(PFM)\$(VARIETY)\sacss.exe: $(PFM)\$(VARIETY)\sacss.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\abqtest.exe: $(PFM)\$(VARIETY)\abqtest.obj \ - $(PFM)\$(VARIETY)\abq.obj $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\segsmss.exe: $(PFM)\$(VARIETY)\segsmss.obj \ + $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\cbstest.exe: $(PFM)\$(VARIETY)\cbstest.obj \ +$(PFM)\$(VARIETY)\steptest.exe: $(PFM)\$(VARIETY)\steptest.obj \ + $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) + +$(PFM)\$(VARIETY)\teletest.exe: $(PFM)\$(VARIETY)\teletest.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\btcv.exe: $(PFM)\$(VARIETY)\btcv.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\mv2test.exe: $(PFM)\$(VARIETY)\mv2test.obj \ - $(PFM)\$(VARIETY)\poolmv2.obj $(PFM)\$(VARIETY)\abq.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\walkt0.exe: $(PFM)\$(VARIETY)\walkt0.obj \ + $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) $(PFM)\$(VARIETY)\zcoll.exe: $(PFM)\$(VARIETY)\zcoll.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) \ @@ -251,18 +251,6 @@ $(PFM)\$(VARIETY)\replaysw.obj: $(PFM)\$(VARIETY)\replay.obj $(ECHO) $@ copy $** $@ >nul: -$(PFM)\$(VARIETY)\messtest.exe: $(PFM)\$(VARIETY)\messtest.obj \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\steptest.exe: $(PFM)\$(VARIETY)\steptest.obj \ - $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\walkt0.exe: $(PFM)\$(VARIETY)\walkt0.obj \ - $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) - -$(PFM)\$(VARIETY)\exposet0.exe: $(PFM)\$(VARIETY)\exposet0.obj \ - $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) - !ENDIF @@ -307,7 +295,7 @@ $(PFM)\$(VARIETY)\sqlite3.obj: # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2002 Ravenbrook Limited . +# Copyright (C) 2001-2013 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/commpre.nmk b/mps/code/commpre.nmk index 47c66943164..eb49c3259b3 100644 --- a/mps/code/commpre.nmk +++ b/mps/code/commpre.nmk @@ -1,4 +1,4 @@ -# commpre.nmk: FIRST COMMON FRAGMENT FOR PLATFORMS USING MV AND NMAKE +# commpre.nmk: FIRST COMMON FRAGMENT FOR PLATFORMS USING NMAKE -*- makefile -*-1 # # $Id$ # Copyright (c) 2001 Ravenbrook Limited. See end of file for license. @@ -14,17 +14,22 @@ # %%PART: When adding a new part, add a new parameter for the files included # in the part # Parameters: -# PFM platform code, e.g. "nti3mv" +# PFM platform code, e.g. "w3i3mv" # PFMDEFS /D options to define platforms preprocessor symbols # to the compiler. Eg "/DOS_NT /DARCH_386 /DBUILD_MVC" -# MPM list of sources which make up the "mpm" part for this -# platform. Each source is stripped of its .c extension +# MPMCOMMON list of sources which make up the "mpm" part for all +# platforms. Each source is stripped of its .c extension # and surrounded in angle brackets (<>) +# MPM as above, plus sources for the "mpm" part for the current +# platform. # PLINTH as above for the "plinth" part # AMC as above for the "amc" part # AMS as above for the "ams" part # LO as above for the "lo" part -# MRG as above for the "mrg" part +# POOLN as above for the "pooln" part +# SNC as above for the "snc" part +# DW as above for the "dw" part +# FMTTEST as above for the "fmttest" part # TESTLIB as above for the "testlib" part # NOISY if defined, causes command to be emitted # @@ -33,11 +38,75 @@ # # To add new targets. varieties, and parts: # Search for the string "%%TARGET", "%%VARIETY", or "%%PART" in this makefile -# and follow the instructions. If you're adding a part, you'll have to change -# the makefile for all the platforms which use this makefile to define the -# source list for that part. +# and follow the instructions. # + +# TARGETS +# +# +# %%TARGET: When adding a new target, add it to one of the variables +# in this section. Library components go in LIB_TARGETS. + +LIB_TARGETS=mps.lib + +# If it is suitable for running regularly (for example, after every +# build) as an automated test case, add it to AUTO_TEST_TARGETS. + +AUTO_TEST_TARGETS=abqtest.exe amcss.exe amcsshe.exe amsss.exe \ + amssshe.exe apss.exe arenacv.exe awlut.exe awluthe.exe btcv.exe \ + cbstest.exe exposet0.exe expt825.exe finalcv.exe finaltest.exe \ + locbwcss.exe lockcov.exe lockutw3.exe locusss.exe locv.exe \ + messtest.exe mpmss.exe mpsicv.exe mv2test.exe poolncv.exe qs.exe \ + sacss.exe segsmss.exe steptest.exe walkt0.exe zmess.exe + +# If it is not runnable as an automated test case, but is buildable, +# add it to OTHER_TEST_TARGETS with a note. +# +# bttest and teletest -- interactive and so cannot be run unattended. +# zcoll -- takes too long to be useful as a regularly run smoke test. + +OTHER_TEST_TARGETS=bttest.exe teletest.exe zcoll.exe + +# Stand-alone programs go in EXTRA_TARGETS. + +EXTRA_TARGETS=mpseventcnv.exe mpseventtxt.exe + +# This target records programs that we were once able to build but +# can't at the moment: +# +# replay -- depends on the EPVM pool. + +UNBUILDABLE_TARGETS=replay.exe + +ALL_TARGETS=$(LIB_TARGETS) $(AUTO_TEST_TARGETS) $(OTHER_TEST_TARGETS) $(EXTRA_TARGETS) + + +# PARAMETERS +# +# +# %%PART: When adding a new part, add the sources for the new part here. + +MPMCOMMON = \ + \ + \ + \ + \ + \ + +PLINTH = +AMC = +AMS = +AWL = +LO = +MVFF = +POOLN = +SNC = +DW = +FMTTEST = +TESTLIB = + + # CHECK PARAMETERS # # @@ -50,8 +119,8 @@ !IFNDEF PFMDEFS !ERROR commpre.nmk: PFMDEFS not defined !ENDIF -!IFNDEF MPM -!ERROR commpre.nmk: MPM not defined +!IFNDEF MPMCOMMON +!ERROR commpre.nmk: MPMCOMMON not defined !ENDIF !IFNDEF PLINTH !ERROR commpre.nmk: PLINTH not defined @@ -166,7 +235,7 @@ LIBFLAGSCOOL = # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2002 Ravenbrook Limited . +# Copyright (C) 2001-2013 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/mpm.c b/mps/code/mpm.c index 4623ca7fad6..04195a859dd 100644 --- a/mps/code/mpm.c +++ b/mps/code/mpm.c @@ -493,7 +493,9 @@ Res WriteF_firstformat_v(mps_lib_FILE *stream, case 'F': { /* function */ WriteFF f = va_arg(args, WriteFF); Byte *b = (Byte *)&f; - /* TODO: Why do we always write these little-endian? */ + /* ISO C forbits casting function pointers to integer, so + decode bytes (see design.writef.f). + TODO: Be smarter about endianness. */ for(i=0; i < sizeof(WriteFF); i++) { res = WriteULongest(stream, (ULongest)(b[i]), 16, (CHAR_BIT + 3) / 4); diff --git a/mps/code/w3i3mv.nmk b/mps/code/w3i3mv.nmk index 6de2c80db0d..49852582f61 100644 --- a/mps/code/w3i3mv.nmk +++ b/mps/code/w3i3mv.nmk @@ -1,7 +1,7 @@ -# w3i3mv.nmk: WINDOWS (INTEL) NMAKE FILE +# w3i3mv.nmk: WINDOWS (IA-32) NMAKE FILE -*- makefile -*- # # $Id$ -# Copyright (c) 2001 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. PFM = w3i3mv @@ -11,31 +11,12 @@ PFM = w3i3mv PFMDEFS = /DCONFIG_PF_STRING="w3i3mv" /DCONFIG_PF_W3I3MV \ /DWIN32 /D_WINDOWS /Gs -MPM = \ - \ - \ - \ - \ -
\ - \ - \ - -PLINTH = -AMC = -AMS = -AWL = -LO = -SNC = -MVFF = -N = -DW = -FMTTEST = -POOLN = -TESTLIB = - - !INCLUDE commpre.nmk +# MPM sources: core plus platform-specific. +MPM = $(MPMCOMMON) + + # Source to object file mappings and CFLAGS amalgamation # @@ -187,7 +168,7 @@ TESTLIBOBJ = $(TESTLIBOBJ0:>=.obj) # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2002 Ravenbrook Limited . +# Copyright (C) 2001-2013 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/w3i6mv.nmk b/mps/code/w3i6mv.nmk index ad238b05284..7df37c29172 100644 --- a/mps/code/w3i6mv.nmk +++ b/mps/code/w3i6mv.nmk @@ -1,7 +1,7 @@ -# w3i6mv.nmk: WINDOWS (x64) NMAKE FILE +# w3i6mv.nmk: WINDOWS (x86-64) NMAKE FILE -*- makefile -*- # # $Id$ -# Copyright (c) 2001 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. PFM = w3i6mv @@ -12,26 +12,8 @@ PFMDEFS = /DCONFIG_PF_STRING="w3i6mv" /DCONFIG_PF_W3I6MV \ /DWIN32 /D_WINDOWS /Gs MASM = ml64 -MPM = \ - \ - \ - \ - \ -
\ - \ - \ - -PLINTH = -AMC = -AMS = -AWL = -LO = -SNC = -MVFF = -DW = -FMTTEST = -POOLN = -TESTLIB = +# MPM sources: core plus platform-specific. +MPM = $(MPMCOMMON) !INCLUDE commpre.nmk @@ -187,7 +169,7 @@ TESTLIBOBJ = $(TESTLIBOBJ0:>=.obj) # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2002 Ravenbrook Limited . +# Copyright (C) 2001-2013 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/design/alloc-frame.txt b/mps/design/alloc-frame.txt index 9e8c46544b4..f4e19d977d3 100644 --- a/mps/design/alloc-frame.txt +++ b/mps/design/alloc-frame.txt @@ -9,6 +9,7 @@ Allocation frame protocol :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: allocation frames; design Introduction @@ -252,8 +253,7 @@ the ``SelectFrameOfAddr()`` operation. _`.fn.client.in-frame`: This function is used by clients to invoke the ``AddrInFrame()`` operation. -``mps_res_t mps_ap_set_frame_class(mps_ap_t buf, mps_frame_class_t -class)`` +``mps_res_t mps_ap_set_frame_class(mps_ap_t buf, mps_frame_class_t class)`` _`.fn.client.set`: This function is used by clients to invoke the ``SetFrameClass()`` operation. diff --git a/mps/design/arena.txt b/mps/design/arena.txt index 38bdb52542d..7560a5a358d 100644 --- a/mps/design/arena.txt +++ b/mps/design/arena.txt @@ -9,6 +9,7 @@ Arena :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: arena; design Introduction diff --git a/mps/design/arenavm.txt b/mps/design/arenavm.txt index 6de3f2e2331..85eb6c9ea3b 100644 --- a/mps/design/arenavm.txt +++ b/mps/design/arenavm.txt @@ -9,7 +9,9 @@ Virtual Memory Arena :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. - +:Index terms: + pair: virtual memory arena; design + pair: VM arena; design Introduction diff --git a/mps/design/bt.txt b/mps/design/bt.txt index a6d734376dd..21894893ee6 100644 --- a/mps/design/bt.txt +++ b/mps/design/bt.txt @@ -9,6 +9,7 @@ Bit tables :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: bit tables; design Introduction diff --git a/mps/design/buffer.txt b/mps/design/buffer.txt index 78dbcfa97af..6c4d35f5f5f 100644 --- a/mps/design/buffer.txt +++ b/mps/design/buffer.txt @@ -9,6 +9,7 @@ Allocation buffers and allocation points :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: buffers; design Introduction diff --git a/mps/design/check.txt b/mps/design/check.txt index a470cce775f..b044e87b6df 100644 --- a/mps/design/check.txt +++ b/mps/design/check.txt @@ -9,6 +9,7 @@ Checking :Status: incomplete design :Revision: $Id$ :Copyright: See section `Copyright and License`_. +:Index terms: pair: checking; design Introduction diff --git a/mps/design/class-interface.txt b/mps/design/class-interface.txt index d130f7ab8fb..20c2b2f649e 100644 --- a/mps/design/class-interface.txt +++ b/mps/design/class-interface.txt @@ -9,6 +9,7 @@ Pool class interface :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: class interface; design Introduction diff --git a/mps/design/collection.txt b/mps/design/collection.txt index 973a8c0f722..75b41629020 100644 --- a/mps/design/collection.txt +++ b/mps/design/collection.txt @@ -9,6 +9,7 @@ Collection framework :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: collection framework; design Introduction diff --git a/mps/design/config.txt b/mps/design/config.txt index 693ffa93249..404db4c6206 100644 --- a/mps/design/config.txt +++ b/mps/design/config.txt @@ -9,6 +9,7 @@ MPS Configuration :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: configuration; design Introduction @@ -57,6 +58,7 @@ variety-reform_ branch. Client-specific customisation of the MPS will be handled in source control, while the MPS source remains generic, to reduce costs and increase reliability. See [RB_2012-09-13]_. +.. _variety-reform: /project/mps/branch/2012-08-15/variety-reform Definitions diff --git a/mps/design/critical-path.txt b/mps/design/critical-path.txt index 505200735c8..6d76dc82991 100644 --- a/mps/design/critical-path.txt +++ b/mps/design/critical-path.txt @@ -8,6 +8,10 @@ The critical path through the MPS :Date: 2012-09-07 :Revision: $Id$ :Copyright: See section `Copyright and License`_. +:Index terms: + single: critical path + single: path; critical + single: Memory Pool System; critical path Introduction diff --git a/mps/design/diag.txt b/mps/design/diag.txt index 0f3a84aeebe..cbc2e2ddfbd 100644 --- a/mps/design/diag.txt +++ b/mps/design/diag.txt @@ -9,6 +9,7 @@ Diagnostic feedback :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: diagnostic feedback; design Introduction diff --git a/mps/design/finalize.txt b/mps/design/finalize.txt index f299a9a8f29..52e5ebbc651 100644 --- a/mps/design/finalize.txt +++ b/mps/design/finalize.txt @@ -9,6 +9,7 @@ Finalization :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: finalization; design Overview diff --git a/mps/design/fix.txt b/mps/design/fix.txt index a48ac477b2b..8f11f5ea2be 100644 --- a/mps/design/fix.txt +++ b/mps/design/fix.txt @@ -9,6 +9,7 @@ The generic fix function :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: fix function; design Introduction diff --git a/mps/design/guide.hex.trans.txt b/mps/design/guide.hex.trans.txt index 6e8777b8f0b..4664b4adcad 100644 --- a/mps/design/guide.hex.trans.txt +++ b/mps/design/guide.hex.trans.txt @@ -10,6 +10,7 @@ Transliterating the alphabet into hexadecimal :Date: 1997-04-11 :Revision: $Id$ :Copyright: See section `Copyright and License`_. +:Index terms: pair: hexadecimal; transliterating Introduction diff --git a/mps/design/guide.impl.c.format.txt b/mps/design/guide.impl.c.format.txt index 18ebb1badd1..50cfe34a4f5 100644 --- a/mps/design/guide.impl.c.format.txt +++ b/mps/design/guide.impl.c.format.txt @@ -10,6 +10,9 @@ C Style -- formatting :Format: rst :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: C language; formatting guide + pair: C language formatting; guide Introduction diff --git a/mps/design/interface-c.txt b/mps/design/interface-c.txt index ce94aa5fd56..fce695d4847 100644 --- a/mps/design/interface-c.txt +++ b/mps/design/interface-c.txt @@ -9,6 +9,7 @@ C interface design :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: C interface; design Introduction diff --git a/mps/design/io.txt b/mps/design/io.txt index 8263e610144..f909b0fe6b2 100644 --- a/mps/design/io.txt +++ b/mps/design/io.txt @@ -9,6 +9,7 @@ I/O subsystem :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: I/O subsystem; design Introduction diff --git a/mps/design/keyword-arguments.txt b/mps/design/keyword-arguments.txt index 2adb02b59c3..3549d6fabb7 100644 --- a/mps/design/keyword-arguments.txt +++ b/mps/design/keyword-arguments.txt @@ -8,6 +8,7 @@ Keyword arguments in the MPS :Date: 2013-05-09 :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: keyword arguments; design Introduction diff --git a/mps/design/lib.txt b/mps/design/lib.txt index d1e2a793b50..f7d651189d6 100644 --- a/mps/design/lib.txt +++ b/mps/design/lib.txt @@ -9,6 +9,7 @@ Library interface :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: library interface; design Introduction diff --git a/mps/design/lock.txt b/mps/design/lock.txt index df81d9f7c82..6b2001c124f 100644 --- a/mps/design/lock.txt +++ b/mps/design/lock.txt @@ -9,6 +9,7 @@ The lock module :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: locking; design Purpose diff --git a/mps/design/locus.txt b/mps/design/locus.txt index 89260ae4b35..7975d4fefda 100644 --- a/mps/design/locus.txt +++ b/mps/design/locus.txt @@ -9,6 +9,7 @@ MPS Configuration :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: locus manager; design Introduction @@ -323,8 +324,7 @@ details, see implementation section and code, or user doc. Loci .... -``Res LocusCreate(Locus *locusReturn, LocusAttrs attrs, ZoneGroup zg, -LocusAllocDesc adesc)`` +``Res LocusCreate(Locus *locusReturn, LocusAttrs attrs, ZoneGroup zg, LocusAllocDesc adesc)`` _`.function.create`: A function to create a locus: ``adesc`` contains the information about the allocation sequences in the locus, ``zg`` is diff --git a/mps/design/message-gc.txt b/mps/design/message-gc.txt index 4a1f4fa7ded..e9682614649 100644 --- a/mps/design/message-gc.txt +++ b/mps/design/message-gc.txt @@ -9,6 +9,7 @@ GC messages :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: garbage collection messages; design Introduction diff --git a/mps/design/message.txt b/mps/design/message.txt index 5b7026c10a8..7bf84004a1c 100644 --- a/mps/design/message.txt +++ b/mps/design/message.txt @@ -9,6 +9,9 @@ Client message protocol :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: messages; design + single: client message protocol Introduction diff --git a/mps/design/object-debug.txt b/mps/design/object-debug.txt index 126f1b69d3a..1e596e53b5a 100644 --- a/mps/design/object-debug.txt +++ b/mps/design/object-debug.txt @@ -9,6 +9,7 @@ Debugging features for client objects :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: debugging; design Introduction diff --git a/mps/design/pool.txt b/mps/design/pool.txt index aca58a3be2b..2db6f5422ee 100644 --- a/mps/design/pool.txt +++ b/mps/design/pool.txt @@ -9,6 +9,7 @@ Pool and pool class mechanisms :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: pool class mechanism; design Definitions diff --git a/mps/design/poolamc.txt b/mps/design/poolamc.txt index 4023e8be978..5d89e8c298f 100644 --- a/mps/design/poolamc.txt +++ b/mps/design/poolamc.txt @@ -9,6 +9,9 @@ AMC pool class :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: AMC pool class; design + single: pool class; AMC design Introduction diff --git a/mps/design/poolams.txt b/mps/design/poolams.txt index 62423359b6f..a30b829b185 100644 --- a/mps/design/poolams.txt +++ b/mps/design/poolams.txt @@ -9,6 +9,9 @@ AMS pool class :Status: draft design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: AMS pool class; design + single: pool class; AMS design Introduction diff --git a/mps/design/poolawl.txt b/mps/design/poolawl.txt index c1320244414..3b1dfd80dc3 100644 --- a/mps/design/poolawl.txt +++ b/mps/design/poolawl.txt @@ -9,6 +9,9 @@ AWL pool class :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: AWL pool class; design + single: pool class; AWL design Introduction diff --git a/mps/design/poollo.txt b/mps/design/poollo.txt index a1d7f14861f..1ef448dd739 100644 --- a/mps/design/poollo.txt +++ b/mps/design/poollo.txt @@ -9,6 +9,9 @@ LO pool class :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: LO pool class; design + single: pool class; LO design Introduction diff --git a/mps/design/poolmfs.txt b/mps/design/poolmfs.txt index 8a00ee706c9..846bcfce924 100644 --- a/mps/design/poolmfs.txt +++ b/mps/design/poolmfs.txt @@ -9,6 +9,9 @@ MFS pool class :Status: Incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: MFS pool class; design + single: pool class; MFS design Overview diff --git a/mps/design/poolmrg.txt b/mps/design/poolmrg.txt index 9425739dfae..24646dc284d 100644 --- a/mps/design/poolmrg.txt +++ b/mps/design/poolmrg.txt @@ -9,6 +9,9 @@ MRG pool class :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: MRG pool class; design + single: pool class; MRG design Introduction diff --git a/mps/design/poolmvff.txt b/mps/design/poolmvff.txt index 366b9b9c810..4edf4c9b788 100644 --- a/mps/design/poolmvff.txt +++ b/mps/design/poolmvff.txt @@ -9,6 +9,9 @@ MVFF pool class :Organization: Harlequin :Status: incomplete doc :Revision: $Id$ +:Index terms: + pair: MVFF pool class; design + single: pool class; MVFF design Introduction @@ -50,11 +53,11 @@ Methods _`.method`: The MVFF pool supports the following methods: -``Res MVFFInit(Pool pool, va_list arg)`` +``Res MVFFInit(Pool pool, Args arg)`` _`.method.init`: This takes six `keyword arguments`_: -.. _`keyword arguments`: keyword-arguments.rst +.. _`keyword arguments`: keyword-arguments ================================== ============================================ Keyword argument Description @@ -117,6 +120,7 @@ Implementation -------------- _`.impl.free-list`: The pool stores its free list in a CBS (see +//gdr-peewit/info.ravenbrook.com/project/mps/branch/2013-05-17/emergency/design/poolmvff.txt `design.mps.cbs `_), failing over in emergencies to a Freelist (see design.mps.freelist) when the CBS cannot allocate new control structures. This is the reason for the alignment restriction above. @@ -131,7 +135,9 @@ object size (in both cases we align up). _`.design.seg-fail`: If allocating a segment fails, we try again with a segment size just large enough for the object we're allocating. This -is in response to request.mps.170186. +is in response to `request.mps.170186`_. + +.. _`request.mps.170186`: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/mps/170186/ Document History diff --git a/mps/design/prot.txt b/mps/design/prot.txt index c7af5787d6e..e845de71f69 100644 --- a/mps/design/prot.txt +++ b/mps/design/prot.txt @@ -9,6 +9,7 @@ The protection module :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: protection interface; design Introduction diff --git a/mps/design/protan.txt b/mps/design/protan.txt index 53c9811a0e7..2e76f0e4351 100644 --- a/mps/design/protan.txt +++ b/mps/design/protan.txt @@ -9,6 +9,9 @@ ANSI implementation of protection module :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: ANSI; protection interface design + pair: ANSI protection interface; design Introduction diff --git a/mps/design/protli.txt b/mps/design/protli.txt index bb40360344b..678239f39ca 100644 --- a/mps/design/protli.txt +++ b/mps/design/protli.txt @@ -9,6 +9,9 @@ Linux implementation of protection module :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: Linux; protection interface design + pair: Linux protection interface; design Introduction diff --git a/mps/design/protocol.txt b/mps/design/protocol.txt index 55bc76f58e6..ee2ff6f5328 100644 --- a/mps/design/protocol.txt +++ b/mps/design/protocol.txt @@ -9,6 +9,7 @@ Protocol inheritance :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: protocol inheritance; design Introduction diff --git a/mps/design/protsu.txt b/mps/design/protsu.txt index e3518c1a2be..971dbdad354 100644 --- a/mps/design/protsu.txt +++ b/mps/design/protsu.txt @@ -9,6 +9,10 @@ SunOS 4 protection module :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: SunOS 4; protection interface design + pair: SunOS 4 protection interface; design + .. warning:: diff --git a/mps/design/pthreadext.txt b/mps/design/pthreadext.txt index c68bde5ba8e..0f045d01f66 100644 --- a/mps/design/pthreadext.txt +++ b/mps/design/pthreadext.txt @@ -9,6 +9,7 @@ POSIX thread extensions :Status: Draft document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: POSIX thread extensions; design Introduction diff --git a/mps/design/reservoir.txt b/mps/design/reservoir.txt index 408f6d9d9ee..5f4c0833e12 100644 --- a/mps/design/reservoir.txt +++ b/mps/design/reservoir.txt @@ -9,6 +9,7 @@ The low-memory reservoir :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: reservoir; design Introduction diff --git a/mps/design/ring.txt b/mps/design/ring.txt index f2acc49864a..d25b4f952bd 100644 --- a/mps/design/ring.txt +++ b/mps/design/ring.txt @@ -9,6 +9,7 @@ Ring data structure :Status: incomplete design :Revision: $Id$ :Copyright: See section `Copyright and License`_. +:Index terms: pair: ring structure; design Introduction diff --git a/mps/design/root.txt b/mps/design/root.txt index 30cc285eed8..078d94f92a3 100644 --- a/mps/design/root.txt +++ b/mps/design/root.txt @@ -9,6 +9,7 @@ Root manager :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: root manager; design Basics diff --git a/mps/design/scan.txt b/mps/design/scan.txt index 047eb6371d2..875858b556e 100644 --- a/mps/design/scan.txt +++ b/mps/design/scan.txt @@ -9,6 +9,7 @@ The generic scanner :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: generic scanner; design Summaries diff --git a/mps/design/seg.txt b/mps/design/seg.txt index 5e7cdd1e529..ca54b78cec9 100644 --- a/mps/design/seg.txt +++ b/mps/design/seg.txt @@ -9,6 +9,7 @@ Segment data structure :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: segments; design Introduction diff --git a/mps/design/shield.txt b/mps/design/shield.txt index 6ac60ecfa58..2d66193d4b0 100644 --- a/mps/design/shield.txt +++ b/mps/design/shield.txt @@ -9,6 +9,7 @@ Shield :Status: incomplete guide :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: shield; design Introduction @@ -46,13 +47,13 @@ by MPS). ``void ShieldRaise(Arena arena, Seg seg, AccessSet mode)`` -Prevent the mutator accessing the memory in the specified mode -(``AccessREAD``, ``AccessWRITE``, or both). + Prevent the mutator accessing the memory in the specified mode + (``AccessREAD``, ``AccessWRITE``, or both). ``void ShieldLower(Arena arena, Seg seg, AccessSet mode)`` -Allow the mutator to access the memory in the specified mode -(``AccessREAD``, ``AccessWRITE``, or both). + Allow the mutator to access the memory in the specified mode + (``AccessREAD``, ``AccessWRITE``, or both). If the mutator attempts an access that hits a shield, the MPS gets a barrier hit (in the form of a fault, interrupt, exception), quickly @@ -65,11 +66,11 @@ this. ``void ShieldSuspend(Arena arena)`` -Stop all registered mutator threads. + Stop all registered mutator threads. ``void ShieldResume(Arena arena)`` -Resume all registered mutator threads. + Resume all registered mutator threads. Control of collector access @@ -108,7 +109,7 @@ calls to ``ShieldExpose()`` on the same segment, as long as each is balanced by a corresponding ``ShieldCover()`` before ``ShieldLeave()`` is called). A usage count is maintained on each segment in ``seg->depth``: a positive "depth" means a positive number of -outstanding reasons why the segment must be exposed to the collector. +outstanding *reasons* why the segment must be exposed to the collector. When the usage count reaches zero, there is no longer any reason the segment should be unprotected, and the Shield could re-instate hardware protection. @@ -116,14 +117,14 @@ hardware protection. However, as a performance-improving hysteresis, the Shield defers re-protection, maintaining a cache of the last ``ShieldCacheSIZE`` times a segment no longer had a reason to be collector-accessible. -Presence in the cache counts as a 'reason': segments in the cache have +Presence in the cache counts as a reason: segments in the cache have ``seg->depth`` increased by one. As segments get pushed out of the -cache, or at ``ShieldLeave()``, this artificial 'reason' is +cache, or at ``ShieldLeave()``, this artificial reason is decremented from ``seg->depth``, and (if ``seg->depth`` is now zero) the deferred reinstatement of hardware protection happens. So whenever hardware protection is temporarily removed to allow -collector access, there is a 'nurse' that will ensure this protection +collector access, there is a *nurse* that will ensure this protection is re-established: the nurse is either the balancing ``ShieldCover()`` call in collector code, or an entry in the shield cache. diff --git a/mps/design/sig.txt b/mps/design/sig.txt index 4013c1bd44c..4953026a44f 100644 --- a/mps/design/sig.txt +++ b/mps/design/sig.txt @@ -9,6 +9,9 @@ Signatures in the MPS :Date: 2013-05-09 :Revision: $Id$ :Copyright: See section `Copyright and License`_. +:Index terms: + pair: structure signatures; design + single: signatures Introduction @@ -24,13 +27,15 @@ defects early. Overview -------- -Signatures are magic numbers which are written into structures when +Signatures are `magic numbers`_ which are written into structures when they are created and invalidated (by overwriting with ``SigInvalid``) when they are destroyed. They provide a limited form of run-time type checking and dynamic scope checking. They are a simplified form of "Structure Marking", a technique used in the Multics filesystem [THVV_1995]_. +.. _`magic numbers`: http://en.wikipedia.org/wiki/Magic_number_(programming) + Definitions ----------- @@ -54,7 +59,7 @@ This is a 32-bit hex constant, spelled according to guide.hex.trans_:: ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEF9811C7340BC6520F3812 -.. _guide.hex.trans: ./guide.hex.trans.txt +.. _guide.hex.trans: guide.hex.trans This allows the structure to be recognised when looking at memory in a hex dump or memory window, or found using memory searches. @@ -97,10 +102,13 @@ Do not do anything else with signatures. See `.rule.purpose`_. Checking -------- The signature is checked in various ways. Every function that takes a -(pointer to) a signed structure should check its argument using the ``AVERT`` -macro. This macro has different definitions depending on how the MPS is -compiled. It may simply check the signature directly, or call the full -checking function for the structure. +(pointer to) a signed structure should check its argument using the +``AVERT`` macro. This macro has different definitions depending on how +the MPS is compiled (see design.mps.config.def.var_). It may simply check +the signature directly, or call the full checking function for the +structure. + +.. _design.mps.config.def.var: config#def-var The checking function for the structure should also validate the signature as its first step using the ``CHECKS()`` macro (see diff --git a/mps/design/splay.txt b/mps/design/splay.txt index 1efbd4800be..8f3fa79d459 100644 --- a/mps/design/splay.txt +++ b/mps/design/splay.txt @@ -9,6 +9,7 @@ Splay trees :Status: draft document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: splay trees; design Introduction diff --git a/mps/design/sso1al.txt b/mps/design/sso1al.txt index 0d48f189d20..60f156f902d 100644 --- a/mps/design/sso1al.txt +++ b/mps/design/sso1al.txt @@ -9,6 +9,9 @@ Stack scanner for Digital Unix on Alpha :Status: draft document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: Digital Unix on Alpha stack scanner; design + pair: Digital Unix on Alpha; stack scanner design .. warning:: diff --git a/mps/design/strategy.txt b/mps/design/strategy.txt new file mode 100644 index 00000000000..a8b94cdd579 --- /dev/null +++ b/mps/design/strategy.txt @@ -0,0 +1,462 @@ +.. mode: -*- rst -*- + +MPS Strategy +============ + +:Tag: design.mps.strategy +:Author: Nick Barnes +:Organization: Ravenbrook Limited +:Date: 2013-06-04 +:Revision: $Id$ +:Copyright: See section `Copyright and License`_. + + +Introduction +------------ + +_`.intro` This is the design of collection strategy for the MPS. + +_`.readership` MPS developers. + + +Overview +-------- + +_`.overview` The MPS uses "strategy" code to make three decisions: + +- when to start a collection trace; + +- what to condemn; + +- how to schedule tracing work. + +This document describes the current strategy, identifies some +weaknesses in it, and outlines some possible future development +directions. + + +Requirements +------------ + +[TODO: source some from req.dylan, or do an up-to-date requirements +analysis -- NB 2013-03-25] + +Garbage collection is a trade-off between time and space: it consumes +some [CPU] time in order to save some [memory] space. Strategy shifts +the balance point. A better strategy will take less time to produce +more space. Examples of good strategy might include: + +- choosing segments to condemn which contain high proportions of dead + objects; + +- starting a trace when a large number of objects have just died; + +- doing enough collection soon enough that the client program never + suffers low-memory problems; + +- using otherwise-idle CPU resources for tracing. + +Conversely, it would be bad strategy to do the reverse of each of +these (condemning live objects; tracing when there's very little +garbage; not collecting enough; tracing when the client program is +busy). + +Abstracting from these notions, requirements on strategy would +relate to: + +- Maximum pause time and other utilization metrics (for example, + bounded mutator utilization, minimum mutator utilization, total MPS + CPU usage); + +- Collecting enough garbage (for example: overall heap size; + low-memory requirements). + +- Allowing client control (for example, client recommendations for + collection timing or condemnation). + +There are other possible strategy considerations which are so far +outside the scope of current strategy and MPS design that this +document disregards them. For example, either inferring or allowing +the client to specify preferred relative object locations ("this +object should be kept in the same cache line as that one"), to improve +cache locality. + +Generations +----------- + +The largest part of the current MPS strategy implementation is the +support for generational GC. Generations are only fully supported for +AMC (and AMCZ) pools. See under "Non-AMC Pools", below, for more +information. + +Data Structures +............... + +The fundamental structure of generational GC is the ``Chain``, +which describes a set of generations. A chain is created by client +code calling ``mps_chain_create()``, specifying the "size" and +"mortality" for each generation. When creating an AMC pool, the +client code must specify the chain which will control collections for +that pool. The same chain may be used for multiple pools. + +Each generation in a chain has a ``GenDesc`` structure, +allocated in an array pointed to from the chain. Each AMC pool has a +set of ``PoolGen`` structures, one per generation. The PoolGens +for each generation point to the GenDesc and are linked together in a +ring on the GenDesc. These structures are (solely?) used to gather +information for strategy decisions. + +The arena has a unique ``GenDesc`` structure, named +``topGen`` and described in comments as "the dynamic generation" +(although in fact it is the *least* dynamic generation). Each AMC +pool has one more PoolGen than there are GenDescs in the chain. The +extra PoolGen refers to this topGen. + +AMC segments have a segment descriptor ``amcSegStruct`` which is +a ``GCSegStruct`` with two additional fields. One field +``segTypeP`` is a pointer either to the per-generation per-pool +``amcGen`` structure (a subclass of ``PoolGen``), or to a +nailboard (which then points to an amcGen). The other field +``new`` is a boolean used for keeping track of memory usage for +strategy reasons (see below under 'Accounting'). The ``amcGen`` +is used for statistics (``->segs``) and forwarding buffers +(``->forward``). + +The AMC pool class only ever allocates a segment in order to fill a +buffer: either the buffer for a client Allocation Point, or a +forwarding buffer. In order to support generational collection, there +is a subclass ``amcBuf`` of ``SegBuf``, with a +``gen`` field (pointing to a ``amcGen``). So in +``AMCBufferFill()`` the generation of the new segment can be +determined. + +When an AMC pool is created, these ``amcGen`` and +``amcBuf`` structures are all created, and the +``amcBuf->gen`` fields initialized so that the forwarding buffer +of each amcGen knows that it belongs to the next "older" amcGen (apart +from the "oldest" amcGen - that which refers to the topGen - whose +forwarding buffer belongs to itself). + +When copying an object in ``AMCFix()``, the object's current +generation is determined (``amcSegGen()``), and the object is +copied to that amcGen's forwarding buffer, using the buffer protocol. +Thus, objects are "promoted" up the chain of generations until they +end up in the topGen, which is shared between all chains and all +pools. + +For statistics and reporting purposes, when ``STATISTICS`` is +on, each AMC pool has an array of ``PageRetStruct``s, one per +trace. This structure has many ``Count`` fields, and is +intended to help to assess AMC page retention code. See job001811. + +Zones +..... + +All collections in the MPS start with condemnation of a complete +``ZoneSet``. Each generation in each chain has a zoneset +associated with it (``chain->gen[N].zones``); the condemned +zoneset is the union of some number of generation's zonesets. It is +condemned by code in the chain system calling +``TraceCondemnZones()``. This is either for all chains +(``ChainCondemnAll()`` called for every chain from +``traceCondemnAll()``) or for some number of generations in a +single chain (``ChainCondemnAuto()`` called from +``TracePoll()``). Note that the condemnation is of every +automatic-pool segment in any zone in the zoneset. It is not limited +to the segments actually associated with the condemned generation(s). + +An attempt is made to use distinct zonesets for different generations. +Whenever a segment is allocated (``AMCBufferFill()``), a +``SegPref`` is created containing the generation number +(obtained from ``amcBuf->gen->pgen->nr``) and passed to +``SegAlloc()``. The arena keeps a zoneset for each generation +number (up to ``VMArenaGenCount``, defined in +``arenavm.c`` to be ``MPS_WORD_WIDTH/2``), and a +``freeSet``. The zoneset for each generation number starts out +empty, and the ``freeSet`` starts out ``ZoneSetUNIV``. +When a segment is allocated with a ``SegPref`` with a generation +number, an attempt is made to allocate it in the corresponding zoneset +(``pagesFindFreeInZones()``). If the zoneset is empty, an +attempt is made to allocate it in the ``freeSet`` zoneset. +After it is allocated, the zones it occupies are removed from the +``freeSet`` and (if there's a generation ``SegPref``) +added to the zoneset for that generation number. + +Note that this zone placement code knows nothing of chains, +generations, pool classes, etc. It is based solely on the generation +*number*, so generations with the same number from different chains +share a zoneset preference for the purpose of placing newly allocated +segments. Combined with the fact that condemnation is per-zone, this +effectively means that generations in distinct chains are collected +together. One consequence of this is that we don't have a very fine +granularity of control over collection: a garbage collection of all +chains together is triggered by the most eager chain. There's no way +for a library or other small part of a client program to arrange +independent collection of a separate pool or chain. + +When ``AMCBufferFill()`` gets the allocated segment back, it +adds it to the zoneset associated with that generation in the pool's +controlling chain. Note that a chain's per-generation zonesets, which +represent the zones in which segments for that generation in that +chain have been placed, are quite distinct from the arena-wide +per-generation-number zonesets, which represent the zones in which +segments for that generation number in any chain have been placed. +The arena-wide per-generation-number zoneset +``vmArena->genZoneSet[N]`` is augmented in +``vmAllocComm()``. The per-chain per-generation zoneset +``chain->gen[N].zones`` is augmented in +``PoolGenUpdateZones()``. Neither kind of zoneset can ever +shrink. + +Accounting +.......... + +- ``gen[N].mortality`` + + - Specified by the client. + - TODO: fill in how this is used. + +- ``gen[N].capacity`` + + - Specified by the client. + - TODO: fill in how this is used. + +- ``amcSeg->new`` + + - TODO: fill this in + +- ``pgen->totalSize``: + + - incremented by ``AMCBufferFill()``; + - decremented by ``amcReclaimNailed()`` and ``AMCReclaim()``; + - added up by ``GenDescTotalSize(gen)``. + +- ``pgen->newSize``: + + - incremented by ``AMCBufferFill()`` (*when not ramping*) and ``AMCRampEnd()``; + - decremented by ``AMCWhiten()``, + - added up by ``GenDescNewSize(gen)``. + +- ``gen[N].proflow``: + + - set to 1.0 by ``ChainCreate()``; + - ``arena->topGen.proflow`` set to 0.0 by ``LocusInit(arena)``; + - *The value of this field is never used*. + + +- ``pgen->newSizeAtCreate``: + + - set by ``traceCopySizes()`` (that is its purpose); + - output by ``TraceStartGenDesc_diag()``. + +Ramps +..... +The intended semantics of ramping are pretty simple. It allows the +client to advise us of periods of large short-lived allocation on a +particular AP. Stuff allocated using that AP during its "ramp" will +probably be dead when the ramp finishes. How the MPS makes use of this +advice is up to us, but for instance we might segregate those objects, +collect them less enthusiastically during the ramp and then more +enthusiastically soon after the ramp finishes. Ramps can nest. + +A ramp is entered by calling:: + + mps_ap_alloc_pattern_begin(ap, mps_alloc_pattern_ramp()) + +or similar, and left in a similar way. + +This is implemented on a per-pool basis, for AMC only (it's ignored by +the other automatic pools). PoolAMC throws away the identity of the AP +specified by the client. The implementation is intended to work by +changing the generational forwarding behaviour, so that there is a "ramp +generation" - one of the regular AMC generations - which forwards to +itself if collected during a ramp (instead of promoting to an older +generation). It also tweaks the strategy calculation code, in a way +with consequences I am documenting elsewhere. + +Right now, the code sets this ramp generation to the last generation +specified in the pool's "chain": it ordinarily forwards to the +"after-ramp" generation, which is the "dynamic generation" (i.e. the +least dynamic generation, i.e. the arena-wide "top generation"). My +recollection, and some mentions in design/poolamc, suggests that the +ramp generation used to be chosen differently from this. + +So far, it doesn't sound too ghastly, I guess, although the subversion +of the generational system seems a little daft. Read on.... + +An AMC pool has a ``rampMode`` (which is really a state of a state +machine), taking one of five values: OUTSIDE, BEGIN, RAMPING, FINISH, +and COLLECTING (actually the enum values are called RampX for these +X). We initialize in OUTSIDE. The pool also has a ``rampCount``, +which is the ramp nesting depth and is used to allow us to ignore ramp +transitions other than the outermost. According to design/poolamc, +there's an invariant (in BEGIN or RAMPING, ``rampCount > 0``; in +COLLECTING or OUTSIDE, ``rampCount == 0``), but this isn't checked in +``AMCCheck()`` and in fact is false for COLLECTING (see below). + +There is a small set of events causing state machine transitions: + +- entering an outermost ramp; +- leaving an outermost ramp; +- condemning any segment of a ramp generation (detected in AMCWhiten); +- reclaiming any AMC segment. + +Here's pseudo-code for all the transition events: + +Entering an outermost ramp: + if not FINISH, go to BEGIN. + +Leaving an outermost ramp: + if RAMPING, go to FINISH. Otherwise, go to OUTSIDE. + +Condemning a ramp generation segment: + If BEGIN, go to RAMPING and make the ramp generation forward + to itself (detach the forwarding buffer and reset its generation). + If FINISH, go to COLLECTING and make the ramp generation + forward to the after-ramp generation. + +Reclaiming any AMC segment: + If COLLECTING: + if ``rampCount > 0``, go to BEGIN. Otherwise go to OUTSIDE. + +Now, some deductions: + +1. When OUTSIDE, the count is always zero, because (a) it starts that +way, and the only ways to go OUTSIDE are (b) by leaving an outermost +ramp (count goes to zero) or (c) by reclaiming when the count is zero. + +2. When BEGIN, the count is never zero (consider the transitions to +BEGIN and the transition to zero). + +3. When RAMPING, the count is never zero (again consider transitions to +RAMPING and the transition to zero). + +4. When FINISH, the count can be anything (the transition to FINISH has +zero count, but the Enter transition when FINISH can change that and +then it can increment to any value). + +5. When COLLECTING, the count can be anything (from the previous fact, +and the transition to COLLECTING). + +6. *This is a bug!!* The ramp generation is not always reset (to forward +to the after-ramp generation). If we get into FINISH and then see +another ramp before the next condemnation of the ramp generation, we +will Enter followed by Leave. The Enter will keep us in FINISH, and +the Leave will take us back to OUTSIDE, skipping the transition to the +COLLECTING state which is what resets the ramp generation forwarding +buffer. [TODO: check whether I made an issue and/or fixed it; NB 2013-06-04] + +The simplest change to fix this is to change the behaviour of the Leave +transition, which should only take us OUTSIDE if we are in BEGIN or +COLLECTING. We should also update design/poolamc to tell the truth, and +check the invariants, which will be these: + + OUTSIDE => zero + BEGIN => non-zero + RAMPING => non-zero + +A cleverer change might radically rearrange the state machine +(e.g. reduce the number of states to three) but that would require +closer design thought and should probably be postponed until we have a +clearer overall strategy plan. + +While I'm writing pseudo-code versions of ramp-related code, I should +mention this other snippet, which is the only other code relating to +ramping (these notes are useful when thinking about the broader strategy +code): + + In ``AMCBufferFill()``, if we're RAMPING, and filling the forwarding + buffer of the ramp generation, and the ramp generation is the + forwarding buffer's generation, set ``amcSeg->new`` to FALSE. Otherwise, + add the segment size to ``poolGen.newSize``. + +And since I've now mentioned the ``amcSeg->new`` flag, here are the only +other uses of that: + +- it initializes as TRUE. + +- When leaving an outermost ramp, go through all the segments in the + pool. Any non-white segment in the rampGen with new set to FALSE has + its size added to ``poolGen->newSize`` and gets new set to TRUE. + +- in ``AMCWhiten()``, if new is TRUE, the segment size is deducted + from ``poolGen.newSize`` and new is set to FALSE. + +Non-AMC Pools +............. + +The implementations of AMS, AWL, and LO pool classes are all aware of +generations (this is necessary because all tracing is driven by the +generational data structures described above), but do not make use of +them. For LO and AWL, when a pool is created, a chain with a single +generation is also created, with size and mortality parameters +hard-wired into the pool-creation function (LOInit, AWLInit). For +AMS, a chain is passed as a pool creation parameter into +``mps_pool_create()``, but this chain must also have only a +single generation (otherwise ``ResPARAM`` is returned). + +Note that these chains are separate from any chain used by an AMC pool +(except in the trivial case when a single-generation chain is used for +both AMC and AMS). Note also that these pools do not use or point to +the ``arena->topGen``, which applies only to AMC. + +Non-AMC pools have no support for ramps. + +Starting a Trace +................ +TODO: Why do we start a trace? How do we choose what to condemn? + + +Trace Progress +.............. +TODO: When do we do some tracing work? How much tracing work do we do? + +Document History +---------------- +- 2013-06-04 NB Checked this in although it's far from complete. + Pasted in my 'ramping notes' from email, which mention some bugs + which I may have fixed (TODO: check this). + +.. _NB: http://www.ravenbrook.com/consultants/nb/ + + +Copyright and License +--------------------- +Copyright © 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/mps/design/telemetry.txt b/mps/design/telemetry.txt index e930905db79..d829fc73be5 100644 --- a/mps/design/telemetry.txt +++ b/mps/design/telemetry.txt @@ -9,6 +9,7 @@ Telemetry :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: telemetry; design Introduction diff --git a/mps/design/thread-manager.txt b/mps/design/thread-manager.txt index d3b78de6a63..70ece6a1548 100644 --- a/mps/design/thread-manager.txt +++ b/mps/design/thread-manager.txt @@ -9,6 +9,7 @@ Thread Manager :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: thread manager; design Purpose diff --git a/mps/design/thread-safety.txt b/mps/design/thread-safety.txt index 4d17d8eafd5..8250eca0304 100644 --- a/mps/design/thread-safety.txt +++ b/mps/design/thread-safety.txt @@ -9,6 +9,7 @@ Thread safety in the MPS :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: thread safety; design Introduction diff --git a/mps/design/trace.txt b/mps/design/trace.txt index 37557e0a8f9..683bcb4231e 100644 --- a/mps/design/trace.txt +++ b/mps/design/trace.txt @@ -9,6 +9,7 @@ Tracer :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: tracer; design Introduction diff --git a/mps/design/type.txt b/mps/design/type.txt index 33e793411e1..eaf99eebaa9 100644 --- a/mps/design/type.txt +++ b/mps/design/type.txt @@ -9,6 +9,7 @@ General MPS types :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: general types; design Introduction diff --git a/mps/design/version-library.txt b/mps/design/version-library.txt index 32c3427a4aa..736a9648e5d 100644 --- a/mps/design/version-library.txt +++ b/mps/design/version-library.txt @@ -9,6 +9,7 @@ Library version mechanism :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: library version mechanism; design Introduction diff --git a/mps/design/version.txt b/mps/design/version.txt index 61dd3e41c4c..f2854f1ea8f 100644 --- a/mps/design/version.txt +++ b/mps/design/version.txt @@ -9,6 +9,9 @@ Software versions :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: + pair: software versions; design + single: versions; design Introduction diff --git a/mps/design/vm.txt b/mps/design/vm.txt index 3e75d50bab5..9f5cbb7c255 100644 --- a/mps/design/vm.txt +++ b/mps/design/vm.txt @@ -9,6 +9,7 @@ Virtual mapping :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: virtual mapping; design Introduction diff --git a/mps/design/vman.txt b/mps/design/vman.txt index cfccf99cc12..0729cc381d8 100644 --- a/mps/design/vman.txt +++ b/mps/design/vman.txt @@ -9,6 +9,8 @@ ANSI fake VM :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: ANSI fake VM; design + _`.intro`: The ANSI fake VM is an implementation of the MPS VM interface (see design.mps.vm) using services provided by the ANSI C diff --git a/mps/design/vmo1.txt b/mps/design/vmo1.txt index ca0eb49c083..1ff9fff1071 100644 --- a/mps/design/vmo1.txt +++ b/mps/design/vmo1.txt @@ -9,6 +9,8 @@ VM for Digital Unix :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: VM for Digital Unix; design + .. warning:: diff --git a/mps/design/vmso.txt b/mps/design/vmso.txt index 9ec47d0967a..62d32e7f6cf 100644 --- a/mps/design/vmso.txt +++ b/mps/design/vmso.txt @@ -9,6 +9,7 @@ VM for Solaris :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: VM for Solaris; design .. warning:: diff --git a/mps/design/writef.txt b/mps/design/writef.txt index 9a346b01e3f..fec4bd72b6e 100644 --- a/mps/design/writef.txt +++ b/mps/design/writef.txt @@ -9,6 +9,7 @@ The WriteF function :Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. +:Index terms: pair: WriteF function; design Introduction @@ -82,7 +83,7 @@ Code Bame Type Example rendering ======= =========== ================== ====================================== ``$A`` address ``Addr`` ``000000019EF60010`` ``$P`` pointer ``void *`` ``000000019EF60100`` -``$F`` function ``void *(*)()`` ``000000019EF60100`` +``$F`` function ``void *(*)()`` ``0001D69E01000000`` (see `.f`_) ``$S`` string ``char *`` ``hello`` ``$C`` character ``char`` ``x`` ``$W`` word ``ULongest`` ``0000000000109AE0`` @@ -99,10 +100,10 @@ incredible snazzy output engine. We only need it for ``Describe()`` methods and assertion messages. At the moment it's a very simple bit of code -- let's keep it that way. -_`.f`: The ``F`` code is used for function pointers. They are -currently printed as a hexadecimal string of the appropriate length -for the platform, and may one day be extended to include function name -lookup. +_`.f`: The ``F`` code is used for function pointers. ISO C forbids casting +function pointers to other types, so the bytes of their representation are +written sequentially, and may have a different endianness to other pointers. +Could be smarter, or even look up function names, but see `.snazzy`_. Document History diff --git a/mps/manual/Makefile b/mps/manual/Makefile index c4dab7a1b4b..ed3544169ba 100644 --- a/mps/manual/Makefile +++ b/mps/manual/Makefile @@ -40,7 +40,8 @@ help: @echo " tools to install a local copy of the Python tools using virtualenv" clean: - -rm -rf $(BUILDDIR)/{changes,devhelp,dirhtml,doctest,doctrees,epub,html,htmlhelp,json,latex,linkcheck,locale,man,pickle,qthelp,singlehtml,texinfo,text,converted} + -rm -rf $(BUILDDIR)/{changes,devhelp,dirhtml,doctest,doctrees,epub,html,htmlhelp,json,latex,linkcheck,locale,man,pickle,qthelp,singlehtml,texinfo,text} + -find $(BUILDDIR)/source/design -name '*.rst' ! -name 'index.rst' ! -name 'old.rst' -exec rm -f '{}' ';' html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html diff --git a/mps/manual/html/_sources/design/alloc-frame.txt b/mps/manual/html/_sources/design/alloc-frame.txt new file mode 100644 index 00000000000..9333d6e4414 --- /dev/null +++ b/mps/manual/html/_sources/design/alloc-frame.txt @@ -0,0 +1,459 @@ +.. _design-alloc-frame: + + +.. index:: + pair: allocation frames; design + + +Allocation frame protocol +========================= + +.. mps:prefix:: design.mps.alloc-frame + + +Introduction +------------ + +:mps:tag:`intro` This document explains the design of the support for +allocation frames in MPS. + +:mps:tag:`readership` This document is intended for any MM developer. + +:mps:tag:`overview` Allocation frames are used for implementing stack pools; +each stack frame corresponds to an allocation frame. Allocation frames +may also be suitable for implementing other sub-pool groupings, such +as generations and ramp allocation patterns. + +:mps:tag:`overview.ambition` We now believe this to be a design that loses +too many advantages of stack allocation for questionable gains. The +requirements are almost entirely based on unanalysed anecdote, instead +of actual clients. + +.. note:: + + We plan to supersede this with a stack pool design at some point + in the future. Pekka P. Pirinen, 2000-03-09. + + +Definitions +----------- + +:mps:tag:`def.alloc-frame` An allocation frame is a generic name for a +device which groups objects together with other objects at allocation +time, and which may have a parent/child relationship with other +allocation frames. + + +Purpose +------- + +:mps:tag:`purpose.stack-allocation` The allocation frame protocol is +intended to support efficient memory management for stack allocation, +that is, the allocation of objects which have dynamic extent. + +:mps:tag:`purpose.general` The allocation frame protocol is intended to be +sufficiently general that it will be useful in supporting other types +of nested allocation patterns too. For example, it could be used to +for EPVM-style save and restore, ramp allocation patterns or +generations. + + +Requirements +------------ + +Known requirements +.................. + +:mps:tag:`req.stack-alloc` Provide a interface for clients to describe a +stack allocation pattern, as an alternative to using the control +stack. + +:mps:tag:`req.efficient` Permit an implementation which is comparable in +efficiency to allocating on the control stack. + +:mps:tag:`req.ap` Support allocation via allocation points (APs). + +:mps:tag:`req.format` Support the allocation of formatted objects. + +:mps:tag:`req.scan` Ensure that objects in allocation frames can participate +in garbage collection by being scanned. + +:mps:tag:`req.fix` Ensure that objects in allocation frames can participate +in garbage collection by accepting Fix requests. + +:mps:tag:`req.condemn` Ensure that objects in allocation frames can +participate in garbage collection by being condemned. + +:mps:tag:`attr.locking` Minimize the synchronization cost for the creation +and destruction of frames. + + +Proto-requirements +.................. + +:mps:tag:`proto-req` The following are possible requirements that might be +important in the future. The design does not necessarily meet all +these requirements, but it does consider them all. Each requirement +either has direct support in the framework, or could be supported with +future additions to the framework. + +:mps:tag:`req.parallels` The allocation frame protocol should provide a +framework for exploiting the parallels between stack extents, +generations and "ramps". + +:mps:tag:`req.pool-destroy` It should be possible to use allocation frames +to free all objects in a pool without destroying the pool. + +:mps:tag:`req.epvm` It should be possible to implement EPVM-style save and +restore operations by creating and destroying allocation frames. + +:mps:tag:`req.subst` It should be possible to substitute a stack pool with a +GC-ed pool so that erroneous use of a stack pool can be detected. + +:mps:tag:`req.format-extensions` It should be possible for stack pools to +utilize the same format as any other pool, including debugging formats +that include fenceposting, etc. + +:mps:tag:`req.mis-nest` Should ensure "mis-nested" stacks are safe. + +:mps:tag:`req.non-top-level` Should support allocation in the non-top stack +extent. + +:mps:tag:`req.copy-if-necessary` Should ensure that stack pools can support +"copy-if-necessary" (so that low-level system code can heapify stack +objects.) + +:mps:tag:`req.preserve` When an object is in an allocation frame which is +being destroyed, it should be possible to preserve that object in the +parent frame. + +:mps:tag:`req.contained` Should allow clients to ask if an object is +"contained" in a frame. The object is contained in a frame if it is +affected when the frame is ended. + +:mps:tag:`req.alloc-with-other` Should allow clients to allocate an object +in the same frame as another object. + + +Overview +-------- + +:mps:tag:`frame-classes` The protocol supports different types of allocation +frames, which are represented as "frame classes". It's up to pools to +determine which classes of allocation frames they support. Pools which +support more than one frame class rely on the client to indicate which +class is currently of interest. The client indicates this by means of +an operation which stores the class in the buffer to which the +allocation point is attached. + +:mps:tag:`frame-handles` Allocation frames are described via abstract "frame +handles". Pools may choose what the representation of a frame handle +should be. Frame handles are static, and the client need not store +them in a GC root. + +:mps:tag:`lightweight-frames` The design includes an extension to the +allocation point protocol, which permits the creation and destruction +of allocation frames without the necessity for claiming the arena +lock. Such frames are called "lightweight frames". + + +Operations +---------- + +:mps:tag:`op.intro` Each operation has both an external (client) interface +and an internal (MPS) interface. The external function takes an +allocation point as a parameter, determines which buffer and pool it +belongs to, and calls the internal function with the buffer and pool +as parameters. + +:mps:tag:`op.obligatory` The following operations are supported on any +allocation point which supports allocation frames:- + +:mps:tag:`operation.push` The :c:func:`PushFrame()` operation creates a new +allocation frame of the currently chosen frame class, makes this new +frame the current frame, and returns a handle for the frame. + +:mps:tag:`operation.pop` The :c:func:`PopFrame()` operation takes a frame handle +as a parameter. Some pool classes might insist or assume that this is +the handle for the current frame. It finds the parent of that frame +and makes it the current frame. The operation indicates that all +children of the new current frame contain objects which are likely to +be dead. The reclaim policy is up to the pool; some classes might +insist or assume that the objects must be dead, and eagerly free them. +Note that this might introduce the possibility of leaving dangling +pointers elsewhere in the arena. If so, it's up to the pool to decide +what to do about this. + +:mps:tag:`op.optional` The following operations are supported for some +allocation frames, but not all. Pools may choose to support some or +all of these operations for certain frame classes. An unsupported +operation will return a failure value:- + +:mps:tag:`operation.select` The :c:func:`SelectFrame()` operation takes a frame +handle as a parameter and makes that frame the current frame. It does +not indicate that any children of the current frame contain objects +which are likely to be dead. + +:mps:tag:`operation.select-addr` The :c:func:`SelectFrameOfAddr()` operation takes +an address as a parameter and makes the frame of that address the +current frame. It does not indicate that any children of the current +frame contain objects which are likely to be dead. + +:mps:tag:`operation.in-frame` The :c:func:`AddrInFrame()` operation determines +whether the supplied address is the address of an object allocated in +the supplied frame, or any child of that frame. + +:mps:tag:`operation.set` The :c:func:`SetFrameClass()` operation takes a frame +class and an allocation point as parameters, and makes that the +current frame class for the allocation point. The next :c:func:`PushFrame()` +operation will create a new frame of that class. + + +Interface +--------- + +External types +.............. + +:mps:tag:`type.client.frame-handle` Frame handles are defined as the abstract +type :c:type:`mps_frame_t`. + +.. c:type:: struct mps_frame_class_s *mps_frame_class_t + +:mps:tag:`type.client.frame-class` Frame classes are defined as an abstract +type. + +:mps:tag:`type.client.frame-class.access` Clients access frame classes by +means of dedicated functions for each frame class. + +External functions +.................. + +:mps:tag:`fn.client.push` :c:func:`mps_ap_frame_push()` is used by clients to +invoke the :c:func:`PushFrame()` operation. For lightweight frames, this +might not invoke the corresponding internal function. + +:mps:tag:`fn.client.pop` :c:func:`mps_ap_frame_pop()` is used by clients to invoke +the :c:func:`PopFrame()` operation. For lightweight frames, this might not +invoke the corresponding internal function. + +.. c:function:: mps_res_t mps_ap_frame_select(mps_ap_t buf, mps_frame_t frame) + +:mps:tag:`fn.client.select` This following function is used by clients to +invoke the :c:func:`SelectFrame()` operation. + +.. c:function:: mps_res_t mps_ap_frame_select_from_addr(mps_ap_t buf, mps_addr_t addr) + +:mps:tag:`fn.client.select-addr` This function is used by clients to invoke +the :c:func:`SelectFrameOfAddr()` operation. + +.. c:function:: mps_res_t mps_ap_addr_in_frame(mps_bool_t *inframe_o, mps_ap_t buf, mps_addr_t *addrref, mps_frame_t frame) + +:mps:tag:`fn.client.in-frame` This function is used by clients to invoke the +:c:func:`AddrInFrame()` operation. + +.. c:function:: mps_res_t mps_ap_set_frame_class(mps_ap_t buf, mps_frame_class_t class) + +:mps:tag:`fn.client.set` This function is used by clients to invoke the +:c:func:`SetFrameClass()` operation. + +.. c:function:: mps_frame_class_t mps_alloc_frame_class_stack(void) + +:mps:tag:`fn.client.stack-frame-class` This function is used by clients to +access the frame class used for simple stack allocation. + + +Internal types +.............. + +.. c:type:: struct AllocFrameStruct *AllocFrame + +:mps:tag:`type.frame-handle` Frame handles are defined as an abstract type. + +.. c:type:: struct AllocFrameClassStruct *AllocFrameClass + +:mps:tag:`type.frame-class` Frame classes are defined as an abstract type. + +.. c:type:: Res (*PoolFramePushMethod)(AllocFrame *frameReturn, Pool pool, Buffer buf) + +:mps:tag:`fn.push` A pool method of this type is called (if needed) to +invoke the :c:func:`PushFrame()` operation. + +.. c:type:: Res (*PoolFramePopMethod)(Pool pool, Buffer buf, AllocFrame frame) + +:mps:tag:`fn.pop` A pool method of this type is called (if needed) +to invoke the PopFrame operation: + +.. c:type:: Res (*PoolFrameSelectMethod)(Pool pool, Buffer buf, AllocFrame frame) + +:mps:tag:`fn.select` A pool method of this type is called to invoke the +:c:func:`SelectFrame()` operation. + +.. c:type:: Res (*PoolFrameSelectFromAddrMethod)(Pool pool, Buffer buf, Addr addr) + +:mps:tag:`fn.select-addr` A pool method of this type is called to invoke the +:c:func:`SelectFrameOfAddr()` operation. + +.. c:type:: Res (*PoolAddrInFrameMethod)(Bool *inframeReturn, Pool pool, Seg seg, Addr *addrref, AllocFrame frame) + +:mps:tag:`fn.in-frame` A pool method of this type is called to invoke the +:c:func:`AddrInFrame()` operation. + +.. c:type:: Res (*PoolSetFrameClassMethod)(Pool pool, Buffer buf, AllocFrameClass class) + +:mps:tag:`fn.set` A pool method of this type is called to invoke the +:c:func:`SetFrameClass()` operation. + + +Lightweight frames +------------------- + +Overview +........ + +:mps:tag:`lw-frame.overview` Allocation points provide direct support for +lightweight frames, and are designed to permit PushFrame and PopFrame +operations without the need for locking and delegation to the pool +method. Pools can disable this mechanism for any allocation point, so +that the pool method is always called. The pool method will be called +whenever synchronization is required for other reasons (e.g. the +buffer is tripped). + +:mps:tag:`lw-frame.model` Lightweight frames offer direct support for a +particular model of allocation frame use, whereby the PushFrame +operation returns the current allocation pointer as a frame handle, +and the PopFrame operation causes the allocation pointer to be reset +to the address of the frame handle. This model should be suitable for +simple stack frames, where more advanced operations like SelectFrame +are not supported. It may also be suitable for more advanced +allocation frame models when they are being used simply. The use of a +complex operation always involves synchronization via locking, and the +pool may disable lightweight synchronization temporarily at this time. + +State +..... + +:mps:tag:`lw-frame.states` Allocation points supporting lightweight frames +will be in one of the following states: + +============ ================================================================ +Valid Indicates that :c:func:`PushFrame()` can be a lightweight + operation and need not be synchronized. +PopPending Indicates that there has been a :c:func:`PopFrame()` operation + that the pool must respond to. +Disabled Indicates that the pool has disabled support for lightweight + operations for this AP. +============ ================================================================ + +These states are in addition to the state normally held by an AP for +allocation purposes. An AP will be in the Disabled state at creation. + +:mps:tag:`lw-frame.transitions` State transitions happen under the following +circumstances: + +======================= ==================================================== +Valid → PopPending As a result of a client :c:func:`PopFrame()` + operation. +Valid → Disabled At the choice of the pool (for example, when + responding to a :c:func:`SelectFrame()` operation). +PopPending → Valid At the choice of the pool, when processing a + :c:func:`PopFrame()`. +PopPending → Disabled At the choice of the pool, when processing a + :c:func:`PopFrame()`. +Disabled → Valid At the choice of the pool. +Disabled → Popframe Illegal. +======================= ==================================================== + +:mps:tag:`lw-frame.state-impl` Each AP contains 3 additional fields to hold this state:: + + mps_addr_t frameptr; + mps_bool_t enabled; + mps_bool_t lwPopPending; + +:mps:tag:`lw-frame.enabled` The ``enabled`` slot holds the following values for +each state: + +========== ========== +Valid :c:macro:`TRUE` +PopPending :c:macro:`TRUE` +Disabled :c:macro:`FALSE` +========== ========== + +:mps:tag:`lw-frame.frameptr` The ``frameptr`` slot holds the following values +for each state: + +========== ============================================ +Valid :c:macro:`NULL` +PopPending Frame handle for most recently popped frame. +Disabled :c:macro:`NULL` +========== ============================================ + +:mps:tag:`lw-frame.lwPopPending` The ``lwPopPending`` slot holds the +following values for each state: + +========== ========= +Valid :c:macro:`FALSE` +PopPending :c:macro:`TRUE` +Disabled :c:macro:`FALSE` +========== ========= + +:mps:tag:`lw-frame.state-for-gc` It is not necessary for the tracer, format +code, pool, or any other part of the GC support in MPS to read either +of the two additional AP fields in order to scan a segment which +supports a lightweight allocation frame. + + +Synchronization +............... + +:mps:tag:`lw-frame.sync` The purpose of the design is that mutator may +access the state of an AP without locking with MPS (via the external +functions). The design assumes the normal MPS restriction that an +operation on an AP may only be performed by a single mutator thread at +a time. Each of the operations on allocation frames counts as an +operation on an AP. + +:mps:tag:`lw-frame.sync.pool` Pools are permitted to read or modify the +lightweight frame state of an AP only in response to an operation on +that AP. + +:mps:tag:`lw-frame.sync.external` The external functions +:c:func:`mps_ap_frame_push()` and :c:func:`mps_ap_frame_pop()` are permitted to +read the values of the ``enabled`` and ``frameptr`` fields for the +supplied AP without claiming the arena lock. They are permitted to +modify the ``frameptr`` field if and only if ``enabled == FALSE``. + +:mps:tag:`lw-frame.sync.trip` When a buffer trip happens, and the trap +wasn't set by MPS itself (that is, it wasn't because of a flip or for +logging), then the buffer code must check whether the AP has state +PopPending. If it does, the buffer code must call the Pool. + + +Implementation +.............. + +:mps:tag:`lw-frame.push` The external :c:func:`PushFrame()` operation +(:c:func:`mps_ap_frame_push()`) performs the following operations:: + + IF (!APIsTrapped(ap) && StateOfFrame(ap) == Valid && ap->init == ap->alloc) + *frame_o = ap->init; + ELSE + WITH_ARENA_LOCK + PerformInternalPushFrameOperation(...) + END + END + +:mps:tag:`lw-frame.pop` The external :c:func:`PopFrame()` operation +(:c:func:`mps_ap_frame_pop()`) performs the following operations:: + + IF (StateOfFrame(ap) != Disabled) + TrapAP(ap); /* ensure next allocation or push involves the pool */ + ap->frameptr = frame; + ap->lwpopPending = TRUE; + ELSE + WITH_ARENA_LOCK + PerformInternalPopFrameOperation(...) + END + END + + diff --git a/mps/manual/html/_sources/design/arena.txt b/mps/manual/html/_sources/design/arena.txt index 6b89e3948c2..c0f671e419d 100644 --- a/mps/manual/html/_sources/design/arena.txt +++ b/mps/manual/html/_sources/design/arena.txt @@ -1,6 +1,535 @@ +.. _design-arena: + + .. index:: pair: arena; design -.. _design-arena: -.. include:: ../../converted/arena.rst +Arena +===== + +.. mps:prefix:: design.mps.arena + + +Introduction +------------ + +:mps:tag:`intro` This is the design of the arena structure. + +:mps:tag:`readership` MPS developers. + + +Overview +-------- + +:mps:tag:`overview` The arena serves two purposes. It is a structure that is +the top-level state of the MPS, and as such contains a lot of fields +which are considered "global". And it provides raw memory to pools. + +An arena belongs to a particular arena class. The class is selected +when the arena is created. Classes encapsulate both policy (such as +how pool placement preferences map into actual placement) and +mechanism (such as where the memory originates: operating system +virtual memory, client provided, or via malloc). Some behaviour +(mostly serving the "top-level datastructure" purpose) is implemented +by generic arena code, and some by arena class code. + + +Definitions +----------- + +:mps:tag:`def.tract` Pools request memory from the arena by calling +:c:func:`ArenaAlloc()`. This returns a block comprising a contiguous sequence +of "tracts". A tract has a specific size (also known as the "arena +alignment", which typically corresponds to the operating system page +size) and all tracts are aligned to that size. "Tract" is also used +for the data structure used to manage tracts. + + +Requirements +------------ + +.. note:: + + Where do these come from? Need to identify and document the + sources of requirements so that they are traceable to client + requirements. Most of these come from the architectural design + (design.mps.architecture) or the fix function design + (design.mps.fix). Richard Brooksby, 1995-08-28. + + They were copied from design.mps.arena.vm(1) and edited slightly. + David Jones, 1999-06-23. + + +Block management +................ + +:mps:tag:`req.fun.block.alloc` The arena must provide allocation of +contiguous blocks of memory. + +:mps:tag:`req.fun.block.free` It must also provide freeing of contiguously +allocated blocks owned by a pool - whether or not the block was +allocated via a single request. + +:mps:tag:`req.attr.block.size.min` The arena must support management of +blocks down to the size of the grain (page) provided by the virtual +mapping interface if a virtual memory interface is being used, or a +comparable size otherwise. + +:mps:tag:`req.attr.block.size.max` It must also support management of blocks +up to the maximum size allowed by the combination of operating system +and architecture. This is derived from req.dylan.attr.obj.max (at +least). + +:mps:tag:`req.attr.block.align.min` The alignment of blocks shall not be less +than :c:macro:`MPS_PF_ALIGN` for the architecture. This is so that pool +classes can conveniently guarantee pool allocated blocks are aligned +to :c:macro:`MPS_PF_ALIGN`. (A trivial requirement.) + +:mps:tag:`req.attr.block.grain.max` The granularity of allocation shall not +be more than the grain size provided by the virtual mapping interface. + + +Address translation +................... + +:mps:tag:`req.fun.trans` The arena must provide a translation from any +address to either an indication that the address is not in any tract +(if that is so) or the following data associated with the tract +containing that address: + +:mps:tag:`req.fun.trans.pool` The pool that allocated the tract. + +:mps:tag:`req.fun.trans.arbitrary` An arbitrary pointer value that the pool +can associate with the tract at any time. + +:mps:tag:`req.fun.trans.white` The tracer whiteness information. That is, a +bit for each active trace that indicates whether this tract is white +(contains white objects). This is required so that the "fix" protocol +can run very quickly. + +:mps:tag:`req.attr.trans.time` The translation shall take no more than @@@@ +[something not very large -- drj 1999-06-23] + + +Iteration protocol +.................. + +:mps:tag:`req.iter` er, there's a tract iteration protocol which is +presumably required for some reason? + + +Arena partition +............... + +:mps:tag:`req.fun.set` The arena must provide a method for approximating sets +of addresses. + +:mps:tag:`req.fun.set.time` The determination of membership shall take no +more than @@@@ [something very small indeed]. (the non-obvious +solution is refsets) + + +Constraints +........... + +:mps:tag:`req.attr.space.overhead` req.dylan.attr.space.struct implies that +the arena must limit the space overhead. The arena is not the only +part that introduces an overhead (pool classes being the next most +obvious), so multiple parts must cooperate in order to meet the +ultimate requirements. + +:mps:tag:`req.attr.time.overhead` Time overhead constraint? + +.. note:: + + How can there be a time "overhead" on a necessary component? David + Jones, 1999-06-23. + + +Architecture +------------ + +Statics +....... + +:mps:tag:`static` There is no higher-level data structure than a arena, so in +order to support several arenas, we have to have some static data in +impl.c.arena. See impl.c.arena.static. + +:mps:tag:`static.init` All the static data items are initialized when the +first arena is created. + +:mps:tag:`static.serial` ``arenaSerial`` is a static :c:type:`Serial`, containing +the serial number of the next arena to be created. The serial of any +existing arena is less than this. + +:mps:tag:`static.ring` ``arenaRing`` is the sentinel of the ring of arenas. + +:mps:tag:`static.ring.init` ``arenaRingInit`` is a :c:type:`Bool` showing whether +the ring of arenas has been initialized. + +:mps:tag:`static.ring.lock` The ring of arenas has to be locked when +traversing the ring, to prevent arenas being added or removed. This is +achieved by using the (non-recursive) global lock facility, provided +by the lock module. + +:mps:tag:`static.check` The statics are checked each time any arena is +checked. + + +Arena classes +............. + +.. c:type:: mps_arena_s *Arena + +:mps:tag:`class` The :c:type:`Arena` data structure is designed to be subclassable +(see design.mps.protocol(0)). Clients can select what arena class +they'd like when instantiating one with :c:func:`mps_arena_create()`. The +arguments to :c:func:`mps_arena_create()` are class-dependent. + +:mps:tag:`class.init` However, the generic :c:func:`ArenaInit()` is called from the +class-specific method, rather than vice versa, because the method is +responsible for allocating the memory for the arena descriptor and the +arena lock in the first place. Likewise, :c:func:`ArenaFinish()` is called +from the finish method. + +:mps:tag:`class.fields` The ``alignment`` (for tract allocations) and +``zoneShift`` (for computing zone sizes and what zone an address is +in) fields in the arena are the responsibility of the each class, and +are initialized by the ``init`` method. The responsibility for +maintaining the ``commitLimit``, ``spareCommitted``, and +``spareCommitLimit`` fields is shared between the (generic) arena and +the arena class. ``commitLimit`` (see :mps:ref:`.commit-limit`) is changed by +the generic arena code, but arena classes are responsible for ensuring +the semantics. For ``spareCommitted`` and ``spareCommitLimit`` see +:mps:ref:`.spare-committed` below. + +:mps:tag:`class.abstract` The basic arena class (:c:type:`AbstractArenaClass`) is +abstract and must not be instantiated. It provides little useful +behaviour, and exists primarily as the root of the tree of arena +classes. Each concrete class must specialize each of the class method +fields, with the exception of the describe method (which has a trivial +implementation) and the ``extend``, ``retract`` and +``spareCommitExceeded`` methods which have non-callable methods for +the benefit of arena classes which don't implement these features. + +:mps:tag:`class.abstract.null` The abstract class does not provide dummy +implementations of those methods which must be overridden. Instead +each abstract method is initialized to :c:macro:`NULL`. + + +Tracts +...... + +:mps:tag:`tract` The arena allocation function :c:func:`ArenaAlloc()` allocates a +block of memory to pools, of a size which is aligned to the arena +alignment. Each alignment unit (grain) of allocation is represented by +a tract. Tracts are the hook on which the segment module is +implemented. Pools which don't use segments may use tracts for +associating their own data with each allocation grain. + +:mps:tag:`tract.structure` The tract structure definition looks like this:: + + typedef struct TractStruct { /* Tract structure */ + Pool pool; /* MUST BE FIRST (design.mps.arena.tract.field.pool) */ + void *p; /* pointer for use of owning pool */ + Addr base; /* Base address of the tract */ + TraceSet white : TRACE_MAX; /* traces for which tract is white */ + unsigned int hasSeg : 1; /* does tract have a seg in p? */ + } TractStruct; + +:mps:tag:`tract.field.pool` The pool field indicates to which pool the tract +has been allocated (:mps:ref:`.req.fun.trans.pool`). Tracts are only valid +when they are allocated to pools. When tracts are not allocated to +pools, arena classes are free to reuse tract objects in undefined +ways. A standard technique is for arena class implementations to +internally describe the objects as a union type of :c:type:`TractStruct` and +some private representation, and to set the pool field to :c:macro:`NULL` +when the tract is not allocated. The pool field must come first so +that the private representation can share a common prefix with +:c:type:`TractStruct`. This permits arena classes to determine from their +private representation whether such an object is allocated or not, +without requiring an extra field. + +:mps:tag:`tract.field.p` The ``p`` field is used by pools to associate tracts +with other data (:mps:ref:`.req.fun.trans.arbitrary`). It's used by the +segment module to indicate which segment a tract belongs to. If a pool +doesn't use segments it may use the ``p`` field for its own purposes. +This field has the non-specific type ``(void *)`` so that pools can +use it for any purpose. + +:mps:tag:`tract.field.hasSeg` The ``hasSeg`` bit-field is a Boolean which +indicates whether the ``p`` field is being used by the segment module. +If this field is :c:macro:`TRUE`, then the value of ``p`` is a :c:type:`Seg`. +``hasSeg`` is typed as an ``unsigned int``, rather than a :c:type:`Bool`. +This ensures that there won't be sign conversion problems when +converting the bit-field value. + +:mps:tag:`tract.field.base` The base field contains the base address of the +memory represented by the tract. + +:mps:tag:`tract.field.white` The white bit-field indicates for which traces +the tract is white (:mps:ref:`.req.fun.trans.white`). This information is also +stored in the segment, but is duplicated here for efficiency during a +call to ``TraceFix`` (see design.mps.trace.fix). + +:mps:tag:`tract.limit` The limit of the tract's memory may be determined by +adding the arena alignment to the base address. + +:mps:tag:`tract.iteration` Iteration over tracts is described in +design.mps.arena.tract-iter(0). + +.. c:function:: Bool TractOfAddr(Tract *tractReturn, Arena arena, Addr addr) + +:mps:tag:`tract.if.tractofaddr` The function :c:func:`TractOfAddr()` finds the tract +corresponding to an address in memory. (See :mps:ref:`.req.fun.trans`.) + +If ``addr`` is an address which has been allocated to some pool, then +:c:func:`TractOfAddr()` returns :c:macro:`TRUE`, and sets ``*tractReturn`` to the +tract corresponding to that address. Otherwise, it returns :c:macro:`FALSE`. +This function is similar to :c:func:`TractOfBaseAddr()` (see +design.mps.arena.tract-iter.if.contig-base) but serves a more general +purpose and is less efficient. + +:mps:tag:`tract.if.TRACT_OF_ADDR` :c:func:`TRACT_OF_ADDR()` is a macro version of +:c:func:`TractOfAddr()`. It's provided for efficiency during a call to +:c:func:`TraceFix()` (see design.mps.trace.fix.tractofaddr). + + +Control pool +............ + +:mps:tag:`pool` Each arena has a "control pool", +``arena->controlPoolStruct``, which is used for allocating MPS control +data structures by calling :c:func:`ControlAlloc()`. + + +Polling +....... + +:mps:tag:`poll` :c:func:`ArenaPoll()` is called "often" by other code (for instance, +on buffer fill or allocation). It is the entry point for doing tracing +work. If the polling clock exceeds a set threshold, and we're not +already doing some tracing work (that is, ``insidePoll`` is not set), +it calls :c:func:`TracePoll()` on all busy traces. + +:mps:tag:`poll.size` The actual clock is ``arena->fillMutatorSize``. This is +because internal allocation is only significant when copy segments are +being allocated, and we don't want to have the pause times to shrink +because of that. There is no current requirement for the trace rate to +guard against running out of memory. + +.. note:: + + Clearly it really ought to: we have a requirement to not run out + of memory (see req.dylan.prot.fail-alloc, req.dylan.prot.consult), + and emergency tracing should not be our only story. David Jones, + 1999-06-22. + +:c:func:`BufferEmpty()` is not taken into account, because the splinter will +rarely be useable for allocation and we are wary of the clock running +backward. + +:mps:tag:`poll.clamp` Polling is disabled when the arena is "clamped", in +which case ``arena->clamped`` is :c:macro:`TRUE`. Clamping the arena prevents +background tracing work, and further new garbage collections from +starting. Clamping and releasing are implemented by the :c:func:`ArenaClamp()` +and :c:func:`ArenaRelease()` methods. + +:mps:tag:`poll.park` The arena is "parked" by clamping it, then polling until +there are no active traces. This finishes all the active collections +and prevents further collection. Parking is implemented by the +:c:func:`ArenaPark()` method. + + +Commit limit +............ + +:mps:tag:`commit-limit` The arena supports a client configurable "commit +limit" which is a limit on the total amount of committed memory. The +generic arena structure contains a field to hold the value of the +commit limit and the implementation provides two functions for +manipulating it: :c:func:`ArenaCommitLimit()` to read it, and +:c:func:`ArenaSetCommitLimit()` to set it. Actually abiding by the contract of +not committing more memory than the commit limit is left up to the +individual arena classes. + +:mps:tag:`commit-limit.err` When allocation from the arena would otherwise +succeed but cause the MPS to use more committed memory than specified +by the commit limit :c:func:`ArenaAlloc()` should refuse the request and +return ``ResCOMMIT_LIMIT``. + +:mps:tag:`commit-limit.err.multi` In the case where an :c:func:`ArenaAlloc()` request +cannot be fulfilled for more than one reason including exceeding the +commit limit then class implementations should strive to return a +result code other than ``ResCOMMIT_LIMIT``. That is, +``ResCOMMIT_LIMIT`` should only be returned if the *only* reason for +failing the :c:func:`ArenaAlloc()` request is that the commit limit would be +exceeded. The client documentation allows implementations to be +ambiguous with respect to which result code in returned in such a +situation however. + + +Spare committed (aka "hysteresis") +.................................. + +:mps:tag:`spare-committed` See :c:func:`mps_arena_spare_committed()`. The generic +arena structure contains two fields for the spare committed memory +fund: ``spareCommitted`` records the total number of spare committed +bytes; ``spareCommitLimit`` records the limit (set by the user) on the +amount of spare committed memory. ``spareCommitted`` is modified by +the arena class but its value is used by the generic arena code. There +are two uses: a getter function for this value is provided through the +MPS interface (:c:func:`mps_arena_spare_commit_limit_set()`), and by the +:c:func:`SetSpareCommitLimit()` function to determine whether the amount of +spare committed memory needs to be reduced. ``spareCommitLimit`` is +manipulated by generic arena code, however the associated semantics +are the responsibility of the class. It is the class's responsibility +to ensure that it doesn't use more spare committed bytes than the +value in ``spareCommitLimit``. + +:mps:tag:`spare-commit-limit` The function :c:func:`ArenaSetSpareCommitLimit()` sets +the ``spareCommitLimit`` field. If the limit is set to a value lower +than the amount of spare committed memory (stored in +``spareCommitted``) then the class specific function +``spareCommitExceeded`` is called. + + +Locks +..... + +:mps:tag:`lock.ring` :c:func:`ArenaAccess()` is called when we fault on a barrier. +The first thing it does is claim the non-recursive global lock to +protect the arena ring (see design.mps.lock(0)). + +:mps:tag:`lock.arena` After the arena ring lock is claimed, :c:func:`ArenaEnter()` is +called on one or more arenas. This claims the lock for that arena. +When the correct arena is identified or we run out of arenas, the lock +on the ring is released. + +:mps:tag:`lock.avoid` Deadlocking is avoided as described below: + +:mps:tag:`lock.avoid.mps` Firstly we require the MPS not to fault (that is, +when any of these locks are held by a thread, that thread does not +fault). + +:mps:tag:`lock.avoid.thread` Secondly, we require that in a multi-threaded +system, memory fault handlers do not suspend threads (although the +faulting thread will, of course, wait for the fault handler to +finish). + +:mps:tag:`lock.avoid.conflict` Thirdly, we avoid conflicting deadlock between +the arena and global locks by ensuring we never claim the arena lock +when the recursive global lock is already held, and we never claim the +binary global lock when the arena lock is held. + + +Location dependencies +..................... + +:mps:tag:`ld` Location dependencies use fields in the arena to maintain a +history of summaries of moved objects, and to keep a notion of time, +so that the staleness of location dependency can be determined. + + +Finalization +............ + +:mps:tag:`final` There is a pool which is optionally (and dynamically) +instantiated to implement finalization. The fields ``finalPool`` and +``isFinalPool`` are used. + + +Implementation +-------------- + + +Tract cache +........... + +:mps:tag:`tract.cache` When tracts are allocated to pools by :c:func:`ArenaAlloc()`, +the first tract of the block and it's base address are cached in arena +fields ``lastTract`` and ``lastTractBase``. The function +:c:func:`TractOfBaseAddr()` (see design.mps.arena.tract-iter.if.block-base(0)) +checks against these cached values and only calls the class method on +a cache miss. This optimizes for the common case where a pool +allocates a block and then iterates over all its tracts (for example, +to attach them to a segment). + +:mps:tag:`tract.uncache` When blocks of memory are freed by pools, +:c:func:`ArenaFree()` checks to see if the cached value for the most recently +allocated tract (see :mps:ref:`.tract.cache`) is being freed. If so, the cache +is invalid, and must be reset. The ``lastTract`` and ``lastTractBase`` +fields are set to :c:macro:`NULL`. + + +Control pool +............ + +:mps:tag:`pool.init` The control pool is initialized by a call to +:c:func:`PoolInit()` during :c:func:`ArenaCreate()`. + +:mps:tag:`pool.ready` All the other fields in the arena are made checkable +before calling :c:func:`PoolInit()`, so :c:func:`PoolInit()` can call +``ArenaCheck(arena)``. The pool itself is, of course, not checkable, +so we have a field ``arena->poolReady``, which is false until after +the return from :c:func:`PoolInit()`. :c:func:`ArenaCheck()` only checks the pool if +``poolReady``. + + +Traces +...... + +:mps:tag:`trace` ``arena->trace[ti]`` is valid if and only if +``TraceSetIsMember(arena->busyTraces, ti)``. + +:mps:tag:`trace.create` Since the arena created by :c:func:`ArenaCreate()` has +``arena->busyTraces = TraceSetEMPTY``, none of the traces are +meaningful. + +:mps:tag:`trace.invalid` Invalid traces have signature ``SigInvalid``, which +can be checked. + + +Polling +....... + +:mps:tag:`poll.fields` There are three fields of a arena used for polling: +``pollThreshold``, ``insidePoll``, and ``clamped`` (see above). +``pollThreshold`` is the threshold for the next poll: it is set at the +end of :c:func:`ArenaPoll()` to the current polling time plus +:c:macro:`ARENA_POLL_MAX`. + + +Location dependencies +..................... + +:mps:tag:`ld.epoch` ``arena->epoch`` is the "current epoch". This is the +number of 'flips' of traces in the arena since the arena was created. +From the mutator's point of view locations change atomically at flip. + +:mps:tag:`ld.history` ``arena->history`` is an array of :c:macro:`ARENA_LD_LENGTH` +elements of type ``RefSet``. These are the summaries of moved objects +since the last :c:macro:`ARENA_LD_LENGTH` epochs. If ``e`` is one of these +recent epochs, then :: + + arena->history[e % ARENA_LD_LENGTH] + +is a summary of (the original locations of) objects moved since epoch +``e``. + +:mps:tag:`ld.prehistory` ``arena->prehistory`` is a ``RefSet`` summarizing +the original locations of all objects ever moved. When considering +whether a really old location dependency is stale, it is compared with +this summary. + + +Roots +..... + +:mps:tag:`root-ring` The arena holds a member of a ring of roots in the +arena. It holds an incremental serial which is the serial of the next +root. + + diff --git a/mps/manual/html/_sources/design/arenavm.txt b/mps/manual/html/_sources/design/arenavm.txt new file mode 100644 index 00000000000..b4f3638928c --- /dev/null +++ b/mps/manual/html/_sources/design/arenavm.txt @@ -0,0 +1,206 @@ +.. _design-arenavm: + + +.. index:: + pair: virtual memory arena; design + pair: VM arena; design + + +Virtual Memory Arena +==================== + +.. mps:prefix:: design.mps.arena.vm + pair: virtual memory arena; design + pair: VM arena; design + + +Introduction +------------ + +:mps:tag:`intro` This document describes the detailed design of the Virtual +Memory Arena Class of the Memory Pool System. The VM Arena Class is +just one class available in the MPS. The generic arena part is +described in design.mps.arena. + + +Overview +-------- + +:mps:tag:`overview` VM arenas provide blocks of memory to all other parts of +the MPS in the form of "tracts" using the virtual mapping interface +(design.mps.vm) to the operating system. The VM Arena Class is not +expected to be provided on platforms that do not have virtual memory +(like MacOS, os.s7(1)). + +:mps:tag:`overview.gc` The VM Arena Class provides some special services on +these blocks in order to facilitate garbage collection: + +:mps:tag:`overview.gc.zone` Allocation of blocks with specific zones. This +means that the generic fix function (design.mps.fix) can use a fast +refset test to eliminate references to addresses that are not in the +condemned set. This assumes that a pool class that uses this placement +appropriately is being used (such as the generation placement policy +used by AMC: see design.mps.poolamc(1)) and that the pool selects the +condemned sets to coincide with zone stripes. + +:mps:tag:`overview.gc.tract` A fast translation from addresses to tract. +(See design.mps.arena.req.fun.trans) + + +Notes +----- + +:mps:tag:`note.refset` Some of this document simply assumes that RefSets +(see the horribly incomplete design.mps.refset) have been chosen as +the solution for design.mps.arena.req.fun.set. It's a lot simpler that +way. Both to write and understand. + + +Requirements +------------ + +Most of the requirements are in fact on the generic arena (see +design.mps.arena.req). However, many of those requirements can only be +met by a suitable arena class design. + +Requirements particular to this arena class: + +Placement +......... + +:mps:tag:`req.fun.place` It must be possible for pools to obtain tracts at +particular addresses. Such addresses shall be declared by the pool +specifying what refset zones the tracts should lie in and what refset +zones the tracts should not lie in. It is acceptable for the arena to +not always honour the request in terms of placement if it has run out +of suitable addresses. + +Arena partition +............... + +:mps:tag:`req.fun.set` See design.mps.arena.req.fun.set. The approximation +to sets of address must cooperate with the placement mechanism in the +way required by :mps:ref:`.req.fun.place` (above). + + +Architecture +------------ + +:mps:tag:`arch.memory` The underlying memory is obtained from whatever +Virtual Memory interface (see design.mps.vm). @@@@ Explain why this is +used. + + +Solution ideas +-------------- + +:mps:tag:`idea.grain` Set the arena granularity to the grain provided by the +virtual mapping module. + +:mps:tag:`idea.mem` Get a single large contiguous address area from the +virtual mapping interface and divide that up. + +:mps:tag:`idea.table` Maintain a table with one entry per grain in order to +provide fast mapping (shift and add) between addresses and table +entries. + +:mps:tag:`idea.table.figure` [missing figure] + +:mps:tag:`idea.map` Store the pointers (:mps:ref:`.req.fun.trans`) in the table +directly for every grain. + +:mps:tag:`idea.zones` Partition the managed address space into zones (see +idea.zones) and provide the set approximation as a reference +signature. + +:mps:tag:`idea.first-fit` Use a simple first-fit allocation policy for +tracts within each zone (:mps:ref:`.idea.zones`). Store the freelist in the +table (:mps:ref:`.idea.table`). + +:mps:tag:`idea.base` Store information about each contiguous area (allocated +of free) in the table entry (:mps:ref:`.idea.table`) corresponding to the base +address of the area. + +:mps:tag:`idea.shadow` Use the table (:mps:ref:`.idea.table`) as a "shadow" of the +operating system's page table. Keep information such as last access, +protection, etc. in this table, since we can't get at this information +otherwise. + +:mps:tag:`idea.barrier` Use the table (:mps:ref:`.idea.table`) to implement the +software barrier. Each segment can have a read and/or write barrier +placed on it by each process. (:mps:tag:`idea.barrier.bits` Store a +bit-pattern which remembers which process protected what.) This will +give a fast translation from a barrier-protected address to the +barrier handler via the process table. + +:mps:tag:`idea.demand-table` For a 1 GiB managed address space with a 4 KiB +page size, the table will have 256K-entries. At, say, four words per +entry, this is 4 MiB of table. Although this is only an 0.4%, the +table shouldn't be preallocated or initially it is an infinite +overhead, and with 1 MiB active, it is a 300% overhead! The address +space for the table should be reserved, but the pages for it mapped +and unmapped on demand. By storing the table in a tract, the status of +the table's pages can be determined by looking at it's own entries in +itself, and thus the translation lookup (:mps:ref:`.req.fun.trans`) is slowed +to two lookups rather than one. + +:mps:tag:`idea.pool` Make the Arena Manager a pool class. Arena +initialization becomes pool creation. Tract allocation becomes +:c:func:`PoolAlloc()`. Other operations become class-specific operations on +the "arena pool". + + +Data structures +--------------- + +:mps:tag:`tables` There are two table data structures: a page table, and an +alloc table. + +:mps:tag:`table.page.map` Each page in the VM has a corresponding page table +entry. + +:mps:tag:`table.page.linear` The table is a linear array of PageStruct +entries; there is a simple mapping between the index in the table and +the base address in the VM. Namely: + +- index to base address: ``base-address = arena-base + (index * page-size)`` +- base address to index: ``index = (base-address - arena-base) / page-size`` + +:mps:tag:`table.page.partial` The table is partially mapped on an +"as-needed" basis. The function :c:func:`unusedTablePages()` identifies +entirely unused pages occupied by the page table itself (that is, +those pages of the page table which are occupied by :c:type:`PageStruct` +objects which all describe free pages). Tract allocation and freeing +use this function to map and unmap the page table with no hysteresis. +(There is restriction on the parameters you may pass to +:c:func:`unusedTablePages()`.) + +:mps:tag:`table.page.tract` Each page table entry contains a tract, which is +only valid if it is allocated to a pool. If it is not allocated to a +pool, the fields of the tract are used for other purposes. (See +design.mps.arena.tract.field.pool) + +:mps:tag:`table.alloc` The alloc table is a simple bit table (implemented +using the BT module, design.mps.bt). + +:mps:tag:`table.alloc.map` Each page in the VM has a corresponding alloc +table entry. + +:mps:tag:`table.alloc.semantics` The bit in the alloc table is set iff the +corresponding page is allocated (to a pool). + + +Notes +----- + +:mps:tag:`fig.page` How the pages in the arena area are represented in the +tables. + +[missing figure] + +:mps:tag:`fig.count` How a count table can be used to partially map the page +table, as proposed in request.dylan.170049.sol.map. + +[missing figure] + + diff --git a/mps/manual/html/_sources/design/bt.txt b/mps/manual/html/_sources/design/bt.txt index f8ac21cb720..6690fb5c439 100644 --- a/mps/manual/html/_sources/design/bt.txt +++ b/mps/manual/html/_sources/design/bt.txt @@ -1,6 +1,709 @@ +.. _design-bt: + + .. index:: pair: bit tables; design -.. _design-bt: -.. include:: ../../converted/bt.rst +Bit tables +========== + +.. mps:prefix:: design.mps.bt + + +Introduction +------------ + +:mps:tag:`intro` This is the design of the Bit Tables module. A Bit Table is a +linear array of bits. A Bit Table of length *n* is indexed using an +integer from 0 up to (but not including) *n*. Each bit in a Bit Table +can hold either the value 0 (aka :c:macro:`FALSE`) or 1 (aka :c:macro:`TRUE`). A +variety of operations are provided including: set, reset, and +retrieve, individual bits; set and reset a contiguous range of bits; +search for a contiguous range of reset bits; making a "negative image" +copy of a range. + +:mps:tag:`readership` MPS developers. + + +Definitions +----------- + +:mps:tag:`def.set` **Set** + + Used as a verb meaning to assign the value 1 or :c:macro:`TRUE` to a bit. + Used descriptively to denote a bit containing the value 1. Note 1 + and :c:macro:`TRUE` are synonyms in MPS C code (see + design.mps.type(0).bool.value). + +:mps:tag:`def.reset` **Reset** + + Used as a verb meaning to assign the value 0 or :c:macro:`FALSE` to a + bit. Used descriptively to denote a bit containing the value 0. + Note 0 and :c:macro:`FALSE` are synonyms in MPS C code (see + design.mps.type(0).bool.value). + +.. note:: + + Consider using "fill/empty" or "mark/clear" instead of + "set/reset", set/reset is probably a hangover from drj's z80 + hacking days -- drj 1999-04-26 + +:mps:tag:`def.bt` **Bit Table** + + A Bit Table is a mapping from [0, *n*) to {0,1} for some *n*, + represented as a linear array of bits. + + _`..def.bt.justify`: They are called *bit tables* because a single + bit is used to encode whether the image of a particular integer + under the map is 0 or 1. + +:mps:tag:`def.range` **Range** + + A contiguous sequence of bits in a Bit Table. Ranges are typically + specified as a *base*--*limit* pair where the range includes the + position specified by the base, but excludes that specified by the + limit. The mathematical interval notation for half-open intervals, + [*base*, *limit*), is used. + + + +Requirements +------------ + +:mps:tag:`req.bit` The storage for a Bit Table of *n* bits shall take no more +than a small constant addition to the storage required for *n* bits. +_`..req.bit.why`: This is so that clients can make some predictions +about how much storage their algorithms use. A small constant is +allowed over the minimal for two reasons: inevitable implementation +overheads (such as only being able to allocate storage in multiples of +32 bits), extra storage for robustness or speed (such as signature and +length fields). + +:mps:tag:`req.create` A means to create Bit Tables. :mps:tag:`req.create.why` Obvious. + +:mps:tag:`req.destroy` A means to destroy Bit Tables. _`.req.destroy.why` +Obvious. + +:mps:tag:`req.ops` The following operations shall be supported: + +* :mps:tag:`req.ops.get` **Get**. Get the value of a bit at a specified + index. + +* :mps:tag:`req.ops.set` **Set**. Set a bit at a specified index. + +* :mps:tag:`req.ops.reset` **Reset**. Reset a bit at a specified index. + +:mps:tag:`req.ops.minimal.why` Get, Set, Reset, are the minimal operations. +All possible mappings can be created and inspected using these +operations. + +* :mps:tag:`req.ops.set.range` **SetRange**. Set a range of bits. + :mps:tag:`req.ops.set.range.why` It's expected that clients will often want + to set a range of bits; providing this operation allows the + implementation of the BT module to make the operation efficient. + +* :mps:tag:`req.ops.reset.range` **ResetRange**. Reset a range of + bits. :mps:tag:`req.ops.reset.range.why` as for SetRange, see + :mps:ref:`.req.ops.set.range.why`. + +* :mps:tag:`req.ops.test.range.set` **IsSetRange**. Test whether a range + of bits are all set. :mps:tag:`req.ops.test.range.set.why` Mostly + for checking. For example, often clients will know that a range they + are about to reset is currently all set, they can use this operation + to assert that fact. + +* :mps:tag:`req.ops.test.range.reset` **IsResetRange**. Test whether a + range of bits are all reset. _`.req.ops.test.range.reset.why` + As for IsSetRange, see :mps:ref:`.req.ops.test.range.set.why`. + +* :mps:tag:`req.ops.find` Find a range (which we'll denote [*i*, *j*)) of at + least *L* reset bits that lies in a specified subrange of the entire + Bit Table. Various find operations are required according to the + (additional) properties of the required range: + + * :mps:tag:`req.ops.find.short.low` **FindShortResetRange**. Of all + candidate ranges, find the range with least *j* (find the leftmost + range that has at least *L* reset bits and return just enough of + that). :mps:tag:`req.ops.find.short.low.why` Required by client and VM + arenas to allocate segments. The arenas implement definite + placement policies (such as lowest addressed segment first) so + they need the lowest (or highest) range that will do. It's not + currently useful to allocate segments larger than the requested + size, so finding a short range is sufficient. + + * :mps:tag:`req.ops.find.short.high` **FindShortResetRangeHigh**. Of all + candidate ranges, find the range with greatest *i* (find the + rightmost range that has at least *L* reset bits and return just + enough of that). :mps:tag:`req.ops.find.short.high.why` Required by arenas + to implement a specific segment placement policy (highest + addressed segment first). + + * :mps:tag:`req.ops.find.long.low` **FindLongResetRange**. Of all candidate + ranges, identify the ranges with least *i* and of those find the + one with greatest *j* (find the leftmost range that has at least + *L* reset bits and return all of it). _`.req.ops.find.long.low.why` + Required by the mark and sweep Pool Classes (AMS, AWL, LO) for + allocating objects (filling a buffer). It's more efficient to fill + a buffer with as much memory as is conveniently possible. There's + no strong reason to find the lowest range but it's bound to have + some beneficial (small) cache effect and makes the algorithm more + predictable. + + * :mps:tag:`req.ops.find.long.high` **FindLongResetRangeHigh**. Provided, + but not required, see :mps:ref:`.non-req.ops.find.long.high`. + +* :mps:tag:`req.ops.copy` Copy a range of bits from one Bit Table to another + Bit Table. Various copy operations are required: + + * :mps:tag:`req.ops.copy.simple` Copy a range of bits from one Bit Table to + the same position in another Bit Table. + :mps:tag:`req.ops.copy.simple.why` Required to support copying of the + tables for the "low" segment during segment merging and splitting, + for pools using tables (for example, ``PoolClassAMS``). + + * :mps:tag:`req.ops.copy.offset` Copy a range of bits from one Bit Table to + an offset position in another Bit Table. + :mps:tag:`req.ops.copy.offset.why` Required to support copying of the + tables for the "high" segment during segment merging and + splitting, for pools which support this (currently none, as of + 2000-01-17). + + * :mps:tag:`req.ops.copy.invert` Copy a range of bits from one Bit Table to + the same position in another Bit Table inverting all the bits in + the target copy. :mps:tag:`req.ops.copy.invert.why` Required by colour + manipulation code in ``PoolClassAMS`` and ``PoolClassLO``. + +:mps:tag:`req.speed` Operations shall take no more than a few memory +operations per bit manipulated. :mps:tag:`req.speed.why` Any slower +would be gratuitous. + +:mps:tag:`req.speed.fast` The following operations shall be very fast: + +* :mps:tag:`req.speed.fast.find.short` FindShortResRange (the + operation used to meet :mps:ref:`.req.ops.find.short.low`) + FindShortResRangeHigh (the operation used to meet + :mps:ref:`.req.ops.find.short.high`). + + :mps:tag:`req.speed.fast.find.short.why` These two are used by the client + arena (design.mps.arena.client) and the VM arena + (design.mps.arena.vm) for finding segments in page tables. The + operation will be used sufficiently often that its speed will + noticeably affect the overall speed of the MPS. They will be called + with a length equal to the number of pages in a segment. Typical + values of this length depend on the pool classes used and their + configuration, but we can expect length to be small (1 to 16) + usually. We can expect the Bit Table to be populated densely where + it is populated at all, that is set bits will tend to be clustered + together in subranges. + +* :mps:tag:`req.speed.fast.find.long` FindLongResRange (the operation + used to meet :mps:ref:`.req.ops.find.long.low`) + + :mps:tag:`req.speed.fast.find.long.why` Used in the allocator for + ``PoolClassAWL`` (design.mps.poolawl(1)), ``PoolClassAMS`` + (design.mps.poolams(2)), ``PoolClassEPVM`` (design.mps.poolepvm(0)). + Of these AWL and EPVM have speed requirements. For AWL the length of + range to be found will be the length of a Dylan table in words. + According to mail.tony.1999-05-05.11-36(0), only + objects are allocated in AWL (though not all objects + are allocated in AWL), and the mean length of an + object is 486 Words. No data for EPVM alas. + +:mps:tag:`req.speed.fast.other.why` We might expect mark and sweep pools to +make use of Bit Tables, the MPS has general requirements to support +efficient mark and sweep pools, so that imposes general speed +requirements on Bit Tables. + + +Non requirements +---------------- + +The following are not requirements but the current design could +support them with little modification or does support them. Often they +used to be requirements, but are no longer, or were added +speculatively or experimentally but aren't currently used. + +* :mps:tag:`non-req.ops.test.range.same` **RangesSame**. Test whether two + ranges that occupy the same positions in different Bit Tables are + the same. This used to be required by ``PoolClassAMS``, but is no + longer. Currently (1999-05-04) the functionality still exists. + +* :mps:tag:`non-req.ops.find.long.high` **FindLongResetRangeHigh**. (see + :mps:ref:`.req.ops.find`) Of all candidate ranges, identify the ranges with + greatest *j* and of those find the one with least *i* (find the + rightmost range that has at least *L* reset bits and return all of + it). Provided for symmetry but only currently used by the BT tests + and ``cbstest.c``. + + +Background +---------- + +:mps:tag:`background` Originally Bit Tables were used and implemented +by ``PoolClassLO`` (design.mps.poollo). It was +decided to lift them out into a separate module when designing the +Pool to manage Dylan Weak Tables which is also a mark and sweep pool +and will make use of Bit Tables (see design.mps.poolawl). +:mps:tag:`background.analysis` analysis.mps.bt(0) contains +some of the analysis of the design decisions that were and were not +made in this document. + + +Clients +------- + +:mps:tag:`clients` Bit Tables are used throughout the MPS but the important +uses are in the client and VM arenas (design.mps.arena.client(0) and +design.mps.arena.vm(1)) a bit table is used to record whether each +page is free or not; several pool classes (``PoolClassLO``, +``PoolClassEPVM``, ``PoolClassAMS``) use bit tables to record which +locations are free and also to store colour. + + +Overview +-------- + +:mps:tag:`over` Mostly, the design is as simple as possible. The significant +complications are iteration (see :mps:ref:`.iteration` below) and searching +(see :mps:ref:`.fun.find-res-range` below) because both of these are required +to be fast. + + +Interface +--------- + +.. c:type:: Word *BT + +:mps:tag:`if.representation.abstract` A Bit Table is represented by the type +:c:type:`BT`. + +:mps:tag:`if.declare` The module declares a type :c:type:`BT` and a prototype for +each of the functions below. The type is declared in impl.h.mpmtypes, +the prototypes are declared in impl.h.mpm. Some of the functions are +in fact implemented as macros in the usual way +(doc.mps.ref-man.if-conv(0).macro.std). + +:mps:tag:`if.general.index` Many of the functions specified below take +indexes. If otherwise unspecified an index must be in the interval [0, +*n*) (note, up to, but not including, *n*) where *n* is the number of +bits in the relevant Bit Table (as passed to the :c:func:`BTCreate()` +function). + +:mps:tag:`if.general.range` Where a range is specified by two indexes (*base* +and *limit*), the index *base*, which specifies the beginning of the +range, must be in the interval [0, *n*), and the index *limit*, which +specifies the end of the range, must be in the interval [1, *n*] (note +can be *n*), and *base* must be strictly less than *limit* (empty +ranges are not allowed). Sometimes *i* and *j* are used instead of +*base* and *limit*. + +.. c:function:: Res BTCreate(BT *btReturn, Arena arena, Count n) + +:mps:tag:`if.create` Attempts to create a table of length ``n`` in the arena +control pool, putting the table in ``*btReturn``. Returns ``ResOK`` if +and only if the table is created OK. The initial values of the bits in +the table are undefined (so the client should probably call +:c:func:`BTResRange()` on the entire range before using the :c:type:`BT`). Meets +:mps:ref:`.req.create`. + +.. c:function:: void BTDestroy(BT t, Arena arena, Count n) + +:mps:tag:`if.destroy` Destroys the table ``t``, which must have been created +with :c:func:`BTCreate()`. The value of argument ``n`` must be same as the +value of the argument passed to :c:func:`BTCreate()`. Meets +:mps:ref:`.req.destroy`. + +.. c:function:: size_t BTSize(Count n) + +:mps:tag:`if.size` ``BTSize(n)`` returns the number of bytes needed for a Bit +Table of ``n`` bits. :c:func:`BTSize()` is a macro, but ``(BTSize)(n)`` will +assert if ``n`` exceeds ``COUNT_MAX - MPS_WORD_WIDTH + 1``. This is +used by clients that allocate storage for the :c:type:`BT` themselves. +Before :c:func:`BTCreate()` and :c:func:`BTDestroy()` were implemented that was the +only way to allocate a Bit Table, but is now deprecated. + +.. c:function:: int BTGet(BT t, Index i) + +:mps:tag:`if.get` ``BTGet(t, i)`` returns the ``i``-th bit of the table ``t`` +(that is, the image of ``i`` under the mapping). Meets +:mps:ref:`.req.ops.get`. + +.. c:function:: void BTSet(BT t, Index i) + +:mps:tag:`if.set` ``BTSet(t, i)`` sets the ``i``-th bit of the table ``t`` (to +1). ``BTGet(t, i)`` will now return 1. Meets :mps:ref:`.req.ops.set`. + +.. c:function:: void BTRes(BT t, Index i) + +:mps:tag:`if.res` ``BTRes(t, i)`` resets the ``i``-th bit of the table ``t`` +(to 0). ``BTGet(t, i)`` will now return 0. Meets :mps:ref:`.req.ops.reset`. + +.. c:function:: void BTSetRange(BT t, Index base, Index limit) + +:mps:tag:`if.set-range` ``BTSetRange(t, base, limit)`` sets the range of bits +[``base``, ``limit``) in the table ``t``. ``BTGet(t, x)`` will now +return 1 for ``base`` ≤ ``x`` < ``limit``. Meets +:mps:ref:`.req.ops.test.range.set`. + +.. c:function:: void BTResRange(BT t, Index base, Index limit) + +:mps:tag:`if.res-range` ``BTResRange(t, base, limit)`` resets the range of +bits [``base``, ``limit``) in the table ``t``. ``BTGet(t, x)`` will +now return 0 for ``base`` ≤ ``x`` < ``limit``. Meets +:mps:ref:`.req.ops.test.range.reset`. + +.. c:function:: Bool BTIsSetRange(BT bt, Index base, Index limit) + +:mps:tag:`if.test.range.set` Returns :c:macro:`TRUE` if all the bits in the range +[``base``, ``limit``) are set, :c:macro:`FALSE` otherwise. Meets +:mps:ref:`.req.ops.test.range.set`. + +.. c:function:: Bool BTIsResRange(BT bt, Index base, Index limit) + +:mps:tag:`if.test.range.reset` Returns :c:macro:`TRUE` if all the bits in the range +[``base``, ``limit``) are reset, :c:macro:`FALSE` otherwise. Meets +:mps:ref:`.req.ops.test.range.reset`. + +``Bool BTRangesSame(BT BTx, BT BTy, Index base, Index limit);`` + +:mps:tag:`if.test.range.same` returns :c:macro:`TRUE` if ``BTGet(BTx,i)`` equals +``BTGet(BTy,i)`` for ``i`` in [``base``, ``limit``), and :c:macro:`FALSE` +otherwise. Meets :mps:ref:`.non-req.ops.test.range.same`. + +:mps:tag:`if.find.general` There are four functions (below) to find reset +ranges. All the functions have the same prototype (for symmetry):: + + Bool find(Index *baseReturn, Index *limitReturn, + BT bt, + Index searchBase, Index searchLimit, + Count length); + +where ``bt`` is the Bit Table in which to search. ``searchBase`` and +``searchLimit`` specify a subset of the Bit Table to use, the +functions will only find ranges that are subsets of [``searchBase``, +``searchLimit``) (when set, ``*baseReturn`` will never be less than +``searchBase`` and ``*limitReturn`` will never be greater than +``searchLimit``). ``searchBase`` and ``searchLimit`` specify a range +that must conform to the general range requirements for a range [*i*, +*j*), as per :mps:ref:`.if.general.range` modified appropriately. ``length`` +is the number of contiguous reset bits to find; it must not be bigger +than ``searchLimit - searchBase`` (that would be silly). If a suitable +range cannot be found the function returns :c:macro:`FALSE` (0) and leaves +``*baseReturn`` and ``*limitReturn`` untouched. If a suitable range is +found then the function returns the range's base in ``*baseReturn`` +and its limit in ``*limitReturn`` and returns :c:macro:`TRUE` (1). + +.. c:function:: Bool BTFindShortResRange(Index *baseReturn, Index *limitReturn, BT bt, Index searchBase, Index searchLimit, Count length) + +:mps:tag:`if.find-short-res-range` Finds a range of reset bits in the table, +starting at ``searchBase`` and working upwards. This function is +intended to meet :mps:ref:`.req.ops.find.short.low` so it will find the +leftmost range that will do, and never finds a range longer than the +requested length (the intention is that it will not waste time +looking). + +.. c:function:: Bool BTFindShortResRangeHigh(Index *baseReturn, Index *limitReturn, BT bt, Index searchBase, Index searchLimit, Count length) + +:mps:tag:`if.find-short-res-range-high` Finds a range of reset bits in the +table, starting at ``searchLimit`` and working downwards. This +function is intended to meet :mps:ref:`.req.ops.find.short.high` so it will +find the rightmost range that will do, and never finds a range longer +than the requested length. + +.. c:function:: Bool BTFindLongResRange(Index *baseReturn, Index *limitReturn, BT bt, Index searchBase, Index searchLimit, Count length) + +:mps:tag:`if.find-long-res-range` Finds a range of reset bits in the table, +starting at ``searchBase`` and working upwards. This function is +intended to meet :mps:ref:`.req.ops.find.long.low` so it will find the +leftmost range that will do and returns all of that range (which can +be longer than the requested length). + +.. c:function:: Bool BTFindLongResRangeHigh(Index *baseReturn, Index *limitReturn, BT bt, Index searchBase, Index searchLimit, Count length) + +:mps:tag:`if.find-long-res-range-high` Finds a range of reset bits in the +table, starting at ``searchLimit`` and working downwards. This +function is intended to meet :mps:ref:`.req.ops.find.long.high` so it will +find the rightmost range that will do and returns all that range +(which can be longer than the requested length). + +.. c:function:: void BTCopyRange(BT fromBT, BT toBT, Index base, Index limit) + +:mps:tag:`if.copy-range` Overwrites the ``i``-th bit of ``toBT`` with the +``i``-th bit of ``fromBT``, for all ``i`` in [``base``, ``limit``). +Meets :mps:ref:`.req.ops.copy.simple`. + +.. c:function:: void BTCopyOffsetRange(BT fromBT, BT toBT, Index fromBase, Index fromLimit, Index toBase, Index toLimit) + +:mps:tag:`if.copy-offset-range` Overwrites the ``i``-th bit of ``toBT`` with +the ``j``-th bit of ``fromBT``, for all ``i`` in [``toBase``, +``toLimit``) and corresponding ``j`` in [``fromBase``, ``fromLimit``). +Each of these ranges must be the same size. This might be +significantly less efficient than :c:func:`BTCopyRange()`. Meets +:mps:ref:`.req.ops.copy.offset`. + +.. c:function:: void BTCopyInvertRange(BT fromBT, BT toBT, Index base, Index limit) + +:mps:tag:`if.copy-invert-range` Overwrites the ``i``-th bit of ``toBT`` with +the inverse of the ``i``-th bit of ``fromBT``, for all ``i`` in +[``base``, ``limit``). Meets :mps:ref:`.req.ops.copy.invert`. + + +Detailed design +--------------- + + +Data structures +............... + +:mps:tag:`datastructure` Bit Tables will be represented as (a pointer to) an +array of :c:type:`Word`. A plain array is used instead of the more usual +design convention of implementing an abstract data type as a structure +with a signature (see guide.impl.c.adt(0)). +:mps:tag:`datastructure.words.justify` The type :c:type:`Word` is used as it will +probably map to the object that can be most efficiently accessed on +any particular platform. :mps:tag:`datastructure.non-adt.justify` The usual +abstract data type convention was not followed because (i) The initial +design (drj) was lazy, (ii) Bit Tables are more likely to come in +convenient powers of two with the extra one or two words overhead. +However, the loss of checking is severe. Perhaps it would be better to +use the usual abstract data type style. + + +Functions +......... + +:mps:tag:`fun.size` :c:func:`BTSize()`. Since a Bit Table is an array of :c:type:`Word`, the +size of a Bit Table of *n* bits is simply the number of words that it +takes to store *n* bits times the number of bytes in a word. This is +``ceiling(n/MPS_WORD_WIDTH)*sizeof(Word).`` :mps:tag:`fun.size.justify` Since +there can be at most ``MPS_WORD_WIDTH - 1`` unused bits in the entire +table, this satisfies :mps:ref:`.req.bit`. + +:mps:tag:`index` The designs for the following functions use a decomposition +of a bit-index, ``i``, into two parts, ``iw``, ``ib``. + +* :mps:tag:`index.word` ``iw`` is the "word-index" which is the index into the + word array of the word that contains the bit referred to by the + bit-index. ``iw = i / MPS_WORD_WIDTH``. Since :c:macro:`MPS_WORD_WIDTH` is + a power of two, this is the same as ``iw = i >> MPS_WORD_SHIFT``. + The latter expression is used in the code. :mps:tag:`index.word.justify` The + compiler is more likely to generate good code without the divide. + +* :mps:tag:`index.sub-word` ``ib`` is the "sub-word-index" which is the index + of the bit referred to by the bit-index in the above word. ``ib = i + % MPS_WORD_WIDTH``. Since :c:macro:`MPS_WORD_WIDTH` is a power of two, this + is the same as ``ib = i & ~((Word)-1<>5) + (i&31); + } + +.. c:function:: ACT_ON_RANGE(Index base, Index limit, single_action, bits_action, word_action) +.. c:function:: ACT_ON_RANGE_HIGH(Index base, Index limit, single_action, bits_action, word_action) + +:mps:tag:`iteration` Many of the following functions involve iteration over +ranges in a Bit Table. This is performed on whole words rather than +individual bits, whenever possible (to improve speed). This is +implemented internally by the macros :c:func:`ACT_ON_RANGE()` and +:c:func:`ACT_ON_RANGE_HIGH()` for iterating over the range forwards and +backwards respectively. These macros do not form part of the interface +of the module, but are used extensively in the implementation. The +macros are often used even when speed is not an issue because it +simplifies the implementation and makes it more uniform. The iteration +macros take the parameters ``base``, ``limit``, ``single_action``, +``bits_action``, and ``word_action``: + +* ``base`` and ``limit`` are of type :c:type:`Index` and define the range of + the iteration. + +* ``single_action`` is the name of a macro which will be used for + iterating over bits in the table individually. This macro must take + a single :c:type:`Index` parameter corresponding to the index for the bit. + The expansion of the macro must not contain ``break`` or + ``continue`` because it will be called from within a loop from the + expansion of :c:func:`ACT_ON_RANGE()`. + +* ``bits_action`` is the name of a macro which will be used for + iterating over part-words. This macro must take parameters + ``wordIndex``, ``base``, ``limit`` where ``wordIndex`` is the index + into the array of words, and ``base`` and ``limit`` define a range + of bits within the indexed word. + +* ``word_action`` is the name of a macro which will be used for + iterating over whole-words. This macro must take the single + parameter ``wordIndex`` which is the index of the whole-word in the + array. The expansion of the macro must not contain ``break`` or + ``continue`` because it will be called from within a loop from the + expansion of :c:func:`ACT_ON_RANGE()`. + +:mps:tag:`iteration.exit` The expansion of the ``single_action``, +``bits_action``, and ``word_action`` macros is allowed to contain +``return`` or ``goto`` to terminate the iteration early. This is used +by the test (:mps:ref:`.fun.test.range.set`) and find (:mps:ref:`.fun.find`) +operations. + +:mps:tag:`iteration.small` If the range is sufficiently small only the +``single_action`` macro will be used, as this is more efficient in +practice. The choice of what constitutes a small range is made +entirely on the basis of experimental performance results (and +currently, 1999-04-27, a "small range" is 6 bits or fewer. See +change.mps.epcore.brisling.160181 for some justification). Otherwise +(for a bigger range) ``bits_action`` is used on the part words at +either end of the range (or the whole of the range it if it fits in a +single word), and ``word_action`` is used on the words that comprise +the inner portion of the range. + +The implementation of :c:func:`ACT_ON_RANGE()` (and :c:func:`ACT_ON_RANGE_HIGH()`) is +simple enough. It decides which macros it should invoke and invokes +them. ``single_action`` and ``word_action`` are invoked inside loops. + +:mps:tag:`fun.get` :c:func:`BTGet()`. The bit-index will be converted in the usual +way, see :mps:ref:`.index`. The relevant :c:type:`Word` will be read out of the Bit +Table and shifted right by the sub-:c:type:`Word` index (this brings the +relevant bit down to the least significant bit of the :c:type:`Word`), the +:c:type:`Word` will then be masked with 1, producing the answer. + +:mps:tag:`fun.set` :c:func:`BTSet()`. + +:mps:tag:`fun.res` :c:func:`BTRes()`. + +In both :c:func:`BTSet()` and :c:func:`BTRes()` a mask is constructed by shifting 1 +left by the sub-word-index (see :mps:ref:`.index`). For :c:func:`BTSet()` the mask is +or-ed into the relevant word (thereby setting a single bit). For +:c:func:`BTRes()` the mask is inverted and and-ed into the relevant word +(thereby resetting a single bit). + +:mps:tag:`fun.set-range` :c:func:`BTSetRange()`. :c:func:`ACT_ON_RANGE()` (see :mps:ref:`.iteration` +above) is used with macros that set a single bit (using :c:func:`BTSet()`), +set a range of bits in a word, and set a whole word. + +:mps:tag:`fun.res-range` :c:func:`BTResRange()` This is implemented similarly to +:c:func:`BTSetRange()` (:mps:ref:`.fun.set-range`) except using :c:func:`BTRes()` and reverse +bit-masking logic. + +:mps:tag:`fun.test.range.set` :c:func:`BTIsSetRange()`. :c:func:`ACT_ON_RANGE()` (see +:mps:ref:`.iteration` above) is used with macros that test whether all the +relevant bits are set; if some of the relevant bits are not set then +``return FALSE`` is used to terminate the iteration early and return +from the :c:func:`BTIsSetRange()` function. If the iteration completes then +:c:macro:`TRUE` is returned. + +:mps:tag:`fun.test.range.reset` :c:func:`BTIsResRange()`. As for :c:func:`BTIsSetRange()` +(:mps:ref:`.fun.test.range.set` above) but testing whether the bits are reset. + +:mps:tag:`fun.test.range.same` :c:func:`BTRangesSame()`. As for :c:func:`BTIsSetRange()` +(:mps:ref:`.fun.test.range.set` above) but testing whether corresponding +ranges in the two Bit Tables are the same. Note there are no speed +requirements, but :c:func:`ACT_ON_RANGE()` is used for simplicity and +uniformity. + +:mps:tag:`fun.find` The four external find functions (:c:func:`BTFindShortResRange()`, +:c:func:`BTFindShortResRangeHigh()`, :c:func:`BTFindLongResRange()`, +:c:func:`BTFindLongResRangeHigh()`) simply call through to one of the two +internal functions: :c:func:`BTFindResRange()` and :c:func:`BTFindResRangeHigh()`. + +.. c:function:: Bool BTFindResRange(Index *baseReturn, Index *limitReturn, BT bt, Index searchBase, Index searchLimit, Count minLength, Count maxLength) +.. c:function:: Bool BTFindResRangeHigh(Index *baseReturn, Index *limitReturn, BT bt, Index searchBase, Index searchLimit, Count minLength, Count maxLength) + +There are two length parameters, one specifying the minimum length of +the range to be found, the other the maximum length. For +:c:func:`BTFindShort()` and :c:func:`BTFindShortHigh()`, ``maxLength`` is equal to +``minLength`` when passed; for :c:func:`BTFindLong()` and :c:func:`BTFindLongHigh()`, +``maxLength` is equal to the maximum possible range, namely +``searchLimit - searchBase``. + +:mps:tag:`fun.find-res-range` :c:func:`BTFindResRange()`. Iterate within the search +boundaries, identifying candidate ranges by searching for a reset bit. +The :ref:`Boyer–Moore algorithm ` is used (it's particularly +easy to implement when there are only two symbols, 0 and 1, in the +alphabet). For each candidate range, iterate backwards over the bits +from the end of the range towards the beginning. If a set bit is +found, this candidate has failed and a new candidate range is +selected. If when scanning for the set bit a range of reset bits was +found before finding the set bit, then this (small) range of reset +bits is used as the start of the next candidate. Additionally the end +of this small range of reset bits (the end of the failed candidate +range) is remembered so that we don't have to iterate over this range +again. But if no reset bits were found in the candidate range, then +iterate again (starting from the end of the failed candidate) to look +for one. If during the backwards search no set bit is found, then we +have found a sufficiently large range of reset bits; now extend the +valid range as far as possible up to the maximum length by iterating +forwards up to the maximum limit looking for a set bit. The iterations +make use of the :c:func:`ACT_ON_RANGE()` and :c:func:`ACT_ON_RANGE_HIGH()` macros, +which can use ``goto`` to effect an early termination of the iteration +when a set/reset (as appropriate) bit is found. The macro +:c:func:`ACTION_FIND_SET_BIT()` is used in the iterations. It efficiently +finds the first (that is, with lowest index or weight) set bit in a +word or subword. + +:mps:tag:`fun.find-res-range.improve` Various other performance improvements +have been suggested in the past, including some from +request.epcore.170534. Here is a list of potential improvements which +all sound plausible, but which have not led to performance +improvements in practice: + +* :mps:tag:`fun.find-res-range.improve.step.partial` When the top index in a + candidate range fails, skip partial words as well as whole words, + using, for example, lookup tables. + +* :mps:tag:`fun.find-res-range.improve.lookup` When testing a candidate run, + examine multiple bits at once (for example, 8), using lookup tables + for (for example) index of first set bit, index of last set bit, + number of reset bits, length of maximum run of reset bits. + +:mps:tag:`fun.find-res-range-high` :c:func:`BTFindResRangeHigh()`. Exactly the same +algorithm as in :c:func:`BTFindResRange()` (see :mps:ref:`.fun.find-res-range` above), +but moving over the table in the opposite direction. + +:mps:tag:`fun.copy-simple-range` :c:func:`BTCopyRange()`. Uses :c:func:`ACT_ON_RANGE()` (see +:mps:ref:`.iteration` above) with the obvious implementation. Should be fast. + +:mps:tag:`fun.copy-offset-range` :c:func:`BTCopyOffsetRange()`. Uses a simple +iteration loop, reading bits with :c:func:`BTGet()` and setting them with +:c:func:`BTSet()`. Doesn't use :c:func:`ACT_ON_RANGE()` because the two ranges will +not, in general, be similarly word-aligned. + +:mps:tag:`fun.copy-invert-range` :c:func:`BTCopyInvertRange()`. Uses :c:func:`ACT_ON_RANGE()` +(see :mps:ref:`.iteration` above) with the obvious implementation. Should be +fast---although there are no speed requirements. + + +Testing +------- + +:mps:tag:`test` The following tests are available or have been used during +development. + +:mps:tag:`test.btcv` ``btcv.c``. This is supposed to be a coverage test, +intended to execute all of the module's code in at least some minimal +way. + +:mps:tag:`test.cbstest` ``cbstest.c``. This was written as a test of the +:c:macro:`CBS` module (design.mps.cbs(2)). It compares the functional +operation of a :c:macro:`CBS` with that of a :c:type:`BT` so is a good functional +test of either module. + +:mps:tag:`test.mmqa.120` MMQA_test_function!210.c. This is used because it has +a fair amount of segment allocation and freeing so exercises the arena +code that uses Bit Tables. + +:mps:tag:`test.bttest` ``bttest.c``. This is an interactive test that can be +used to exercise some of the :c:type:`BT` functionality by hand. + +:mps:tag:`test.dylan` It is possible to modify Dylan so that it uses Bit +Tables more extensively. See change.mps.epcore.brisling.160181 TEST1 +and TEST2. + + diff --git a/mps/manual/html/_sources/design/buffer.txt b/mps/manual/html/_sources/design/buffer.txt new file mode 100644 index 00000000000..feaa3142a41 --- /dev/null +++ b/mps/manual/html/_sources/design/buffer.txt @@ -0,0 +1,698 @@ +.. _design-buffer: + + +.. index:: + pair: buffers; design + + +Allocation buffers and allocation points +======================================== + +.. mps:prefix:: design.mps.buffer + + +Introduction +------------ + +:mps:tag:`scope` This document describes the design of allocation buffers +and allocation points. + +:mps:tag:`purpose` The purpose of this document is to record design +decisions made concerning allocation buffers and allocation points and +justify those decisions in terms of requirements. + +:mps:tag:`readership` The document is intended for reading by any MPS +developer. + + +Glossary +-------- + +trapped + + :mps:tag:`def.trapped` The buffer is in a state such that the MPS gets + to know about the next use of that buffer. + + +Source +------ + +:mps:tag:`source.mail` Much of the juicy stuff about buffers is only +floating around in mail discussions. You might like to try searching +the archives if you can't find what you want here. + +.. note:: + + Mail archives are only accessible to Ravenbrook staff. RHSK + 2006-06-09. + +:mps:tag:`source.synchronize` For a discussion of the synchronization +issues: + +* `mail.richard.1995-05-19.17-10 `_ +* `mail.ptw.1995-05-19.19-15 `_ +* `mail.richard.1995-05-24.10-18 `_ + +.. note:: + + I believe that the sequence for flip in PTW's message is + incorrect. The operations should be in the other order. DRJ. + +:mps:tag:`source.interface` For a description of the buffer interface in C +prototypes: + +* `mail.richard.1997-04-28.09-25(0) `_ + +:mps:tag:`source.qa` Discussions with QA were useful in pinning down the +semantics and understanding of some obscure but important boundary +cases. See the thread starting +`mail.richard.tucker.1997-05-12.09-45(0) +`_ with subject "notes on +our allocation points discussion": + +* `mail.richard.tucker.1997-05-12.09-45 `_ +* `mail.ptw.1997-05-12.12-46(1) `_ +* `mail.richard.1997-05-12.13-15 `_ +* `mail.richard.1997-05-12.13-28 `_ +* `mail.ptw.1997-05-13.15-15 `_ +* `mail.sheep.1997-05-14.11-52 `_ +* `mail.rit.1997-05-15.09-19 `_ +* `mail.ptw.1997-05-15.21-22 `_ +* `mail.ptw.1997-05-15.21-35 `_ +* `mail.rit.1997-05-16.08-02 `_ +* `mail.rit.1997-05-16.08-42 `_ +* `mail.ptw.1997-05-16.12-36 `_ +* `mail.ptw.1997-05-16.12-47 `_ +* `mail.richard.1997-05-19.15-46 `_ +* `mail.richard.1997-05-19.15-56 `_ +* `mail.ptw.1997-05-20.20-47 `_ + + + +Requirements +------------ + +:mps:tag:`req.fast` Allocation must be very fast. + +:mps:tag:`req.thread-safe` Must run safely in a multi-threaded environment. + +:mps:tag:`req.no-synch` Must avoid the use of thread-synchronization. +(:mps:ref:`.req.fast`) + +:mps:tag:`req.manual` Support manual memory management. + +:mps:tag:`req.exact` Support exact collectors. + +:mps:tag:`req.ambig` Support ambiguous collectors. + +:mps:tag:`req.count` Must record (approximately) the amount of allocation (in bytes). + +.. note:: + + Actually not a requirement any more, but once was put forward as a + Dylan requirement. Bits of the code still reflect this + requirement. See request.dylan.170554. + + +Classes +------- + +:mps:tag:`class.hierarchy` The :c:type:`Buffer` data structure is designed to be +subclassable (see design.mps.protocol). + +:mps:tag:`class.hierarchy.buffer` The basic buffer class (:c:type:`BufferClass`) +supports basic allocation-point buffering, and is appropriate for +those manual pools which don't use segments (:mps:ref:`.req.manual`). The +:c:type:`Buffer` class doesn't support reference ranks (that is, the buffers +have ``RankSetEMPTY``). Clients may use :c:type:`BufferClass` directly, or +create their own subclasses (see :mps:ref:`.subclassing`). + +:mps:tag:`class.hierarchy.segbuf` Class :c:type:`SegBufClass` is also provided for +the use of pools which additionally need to associate buffers with +segments. :c:type:`SegBufClass` is a subclass of :c:type:`BufferClass`. Manual +pools may find it convenient to use :c:type:`SegBufClass`, but it is +primarily intended for automatic pools (:mps:ref:`.req.exact`, :mps:ref:`.req.ambig`). +An instance of :c:type:`SegBufClass` may be attached to a region of memory +that lies within a single segment. The segment is associated with the +buffer, and may be accessed with the :c:func:`BufferSeg()` function. +:c:type:`SegBufClass` also supports references at any rank set. Hence this +class or one of its subclasses should be used by all automatic pools +(with the possible exception of leaf pools). The rank sets of buffers +and the segments they are attached to must match. Clients may use +:c:type:`SegBufClass` directly, or create their own subclasses (see +:mps:ref:`.subclassing`). + +:mps:tag:`class.hierarchy.rankbuf` Class :c:type:`RankBufClass` is also provided +as a subclass of :c:type:`SegBufClass`. The only way in which this differs +from its superclass is that the rankset of a :c:type:`RankBufClass` is set +during initialization to the singleton rank passed as an additional +parameter to :c:func:`BufferCreate()`. Instances of :c:type:`RankBufClass` are of +the same type as instances of :c:type:`SegBufClass`, that is, ``SegBuf``. +Clients may use :c:type:`RankBufClass` directly, or create their own +subclasses (see :mps:ref:`.subclassing`). + +:mps:tag:`class.create` The buffer creation functions (:c:func:`BufferCreate()` +and :c:func:`BufferCreateV()`) take a ``class`` parameter, which determines +the class of buffer to be created. + +:mps:tag:`class.choice` Pools which support buffered allocation should +specify a default class for buffers. This class will be used when a +buffer is created in the normal fashion by MPS clients (for example by +a call to :c:func:`mps_ap_create()`). Pools specify the default class by +means of the ``bufferClass`` field in the pool class object. This +should be a pointer to a function of type :c:type:`PoolBufferClassMethod`. +The normal class "Ensure" function (for example +:c:func:`EnsureBufferClass()`) has the appropriate type. + +:mps:tag:`subclassing` Pools may create their own subclasses of the standard +buffer classes. This is sometimes useful if the pool needs to add an +extra field to the buffer. The convenience macro +:c:func:`DEFINE_BUFFER_CLASS()` may be used to define subclasses of buffer +classes. See design.mps.protocol.int.define-special. + +:mps:tag:`replay` To work with the allocation replayer (see +design.mps.telemetry.replayer), the buffer class has to emit an event +for each call to an external interface, containing all the parameters +passed by the user. If a new event type is required to carry this +information, the replayer (impl.c.eventrep) must then be extended to +recreate the call. + +:mps:tag:`replay.pool-buffer` The replayer must also be updated if the +association of buffer class to pool or the buffer class hierarchy is +changed. + +:mps:tag:`class.method` Buffer classes provide the following methods (these +should not be confused with the pool class methods related to the +buffer protocol, described in :mps:ref:`.method`): + +.. c:type:: Res (*BufferInitMethod)(Buffer buffer, Pool pool, ArgList args) + +:mps:tag:`class.method.init` :c:func:`init()` is a class-specific initialization +method called from :c:func:`BufferInit()`. It receives the keyword arguments +passed to to :c:func:`BufferInit()`. Client-defined methods must call their +superclass method (via a next-method call) before performing any +class-specific behaviour. :mps:tag:`replay.init` The :c:func:`init()` method +should emit a ``BufferInit`` event (if there aren't any extra +parameters, `` = ""``). + +.. c:type:: void (*BufferFinishMethod)(Buffer buffer) + +:mps:tag:`class.method.finish` :c:func:`finish()` is a class-specific finish +method called from :c:func:`BufferFinish()`. Client-defined methods must +call their superclass method (via a next-method call) after performing +any class-specific behaviour. + +.. c:type:: void (*BufferAttachMethod)(Buffer buffer, Addr base, Addr limit, Addr init, Size size) + +:mps:tag:`class.method.attach` :c:func:`attach()` is a class-specific method +called whenever a buffer is attached to memory, via +:c:func:`BufferAttach()`. Client-defined methods must call their superclass +method (via a next-method call) before performing any class-specific +behaviour. + +.. c:type:: void (*BufferDetachMethod)(Buffer buffer) + +:mps:tag:`class.method.detach` :c:func:`detach()` is a class-specific method +called whenever a buffer is detached from memory, via +:c:func:`BufferDetach()`. Client-defined methods must call their superclass +method (via a next-method call) after performing any class-specific +behaviour. + +.. c:type:: Seg (*BufferSegMethod)(Buffer buffer) + +:mps:tag:`class.method.seg` :c:func:`seg()` is a class-specific accessor method +which returns the segment attached to a buffer (or :c:macro:`NULL` if there +isn't one). It is called from :c:func:`BufferSeg()`. Clients should not need +to define their own methods for this. + +.. c:type:: RankSet (*BufferRankSetMethod)(Buffer buffer) + +:mps:tag:`class.method.rankSet` :c:func:`rankSet()` is a class-specific accessor +method which returns the rank set of a buffer. It is called from +:c:func:`BufferRankSet()`. Clients should not need to define their own +methods for this. + +.. c:type:: void (*BufferSetRankSetMethod)(Buffer buffer, RankSet rankSet) + +:mps:tag:`class.method.setRankSet` :c:func:`setRankSet()` is a class-specific +setter method which sets the rank set of a buffer. It is called from +:c:func:`BufferSetRankSet()`. Clients should not need to define their own +methods for this. + +.. c:type:: Res (*BufferDescribeMethod)(Buffer buffer, mps_lib_FILE *stream) + +:mps:tag:`class.method.describe` :c:func:`describe()` is a class-specific method +called to describe a buffer, via BufferDescribe. Client-defined +methods must call their superclass method (via a next-method call) +before describing any class-specific state. + + +Logging +------- + +:mps:tag:`logging.control` Buffers have a separate control for whether they +are logged or not, this is because they are particularly high volume. +This is a Boolean flag (``bufferLogging``) in the :c:type:`ArenaStruct`. + + +Measurement +----------- + +:mps:tag:`count` Counting the allocation volume is done by maintaining two +fields in the buffer struct: + +:mps:tag:`count.fields` ``fillSize``, ``emptySize``. + +:mps:tag:`count.monotonic` both of these fields are monotonically +increasing. + +:mps:tag:`count.fillsize` ``fillSize`` is an accumulated total of the size +of all the fills (as a result of calling the :c:type:`PoolClass` +:c:func:`BufferFill()` method) that happen on the buffer. + +:mps:tag:`count.emptysize` ``emptySize`` is an accumulated total of the size of +all the empties than happen on the buffer (which are notified to the +pool using the :c:type:`PoolClass` :c:func:`BufferEmpty()` method). + +:mps:tag:`count.generic` These fields are maintained by the generic buffer +code in :c:func:`BufferAttach()` and :c:func:`BufferDetach()`. + +:mps:tag:`count.other` Similar count fields are maintained in the pool and +the arena. They are maintained on an internal (buffers used internally +by the MPS) and external (buffers used for mutator allocation points) +basis. The fields are also updated by the buffer code. The fields are: + +- in the pool: ``fillMutatorSize``, ``fillInternalSize``, + ``emptyMutatorSize``, and ``emptyInternalSize`` (4 fields); +- in the arena, ``fillMutatorSize``, ``fillInternalSize``, + ``emptyMutatorSize``, ``emptyInternalSize``, and + ``allocMutatorSize`` (5 fields). + +:mps:tag:`count.alloc.how` The amount of allocation in the buffer just +after an empty is ``fillSize - emptySize``. At other times this +computation will include space that the buffer has the use of (between +base and init) but which may not get allocated in (because the +remaining space may be too large for the next reserve so some or all +of it may get emptied). The arena field ``allocMutatorSize`` is +incremented by the allocated size (between base and init) +whenever a buffer is detached. Symmetrically this field is decremented +by by the pre-allocated size (between base and init) whenever +a buffer is attached. The overall count is asymptotically correct. + +:mps:tag:`count.type` All the count fields are type double. + +:mps:tag:`count.type.justify` This is because double is the type most likely +to give us enough precision. Because of the lack of genuine +requirements the type isn't so important. It's nice to have it more +precise than long. Which double usually is. + + +Notes from the whiteboard +------------------------- + +Requirements + +- atomic update of words +- guarantee order of reads and write to certain memory locations. + +Flip + +- limit:=0 +- record init for scanner + +Commit + +- init:=alloc +- if(limit = 0) ... +- L written only by MM +- A written only by client (except during synchronized MM op) +- I ditto +- I read by MM during flip + +States + +- busy +- ready +- trapped +- reset + +.. note:: + + There are many more states. DRJ. + +Misc + +- During buffer ops all field values can change. Might trash perfectly + good ("valid"?) object if pool isn't careful. + + +Synchronization +--------------- + +Buffers provide a loose form of synchronization between the mutator +and the collector. + +The crucial synchronization issues are between the operation the pool +performs on flip and the mutator's commit operation. + +Commit + +- read init +- write init +- Memory Barrier +- read ``limit`` + +Flip + +- write ``limit`` +- Memory Barrier +- read init + +Commit consists of two parts. The first is the update to init. +This is a declaration that the new object just before init is now +correctly formatted and can be scanned. The second is a check to see +if the buffer has been "tripped". The ordering of the two parts is +crucial. + +Note that the declaration that the object is correctly formatted is +independent of whether the buffer has been tripped or not. In +particular a pool can scan up to the init pointer (including the newly +declared object) whether or not the pool will cause the commit to +fail. In the case where the pool scans the object, but then causes the +commit to fail (and presumably the allocation to occur somewhere +else), the pool will have scanned a "dead" object, but this is just +another example of conservatism in the general sense. + +Not that the read of init in the Flip sequence can in fact be +arbitrarily delayed (as long as it is read before a buffered segment +is scanned). + +On processors with Relaxed Memory Order (such as the DEC Alpha), +Memory Barriers will need to be placed at the points indicated. + +:: + + * DESIGN + * + * design.mps.buffer. + * + * An allocation buffer is an interface to a pool which provides + * very fast allocation, and defers the need for synchronization in + * a multi-threaded environment. + * + * Pools which contain formatted objects must be synchronized so + * that the pool can know when an object is valid. Allocation from + * such pools is done in two stages: reserve and commit. The client + * first reserves memory, then initializes it, then commits. + * Committing the memory declares that it contains a valid formatted + * object. Under certain conditions, some pools may cause the + * commit operation to fail. (See the documentation for the pool.) + * Failure to commit indicates that the whole allocation failed and + * must be restarted. When using a pool which introduces the + * possibility of commit failing, the allocation sequence could look + * something like this: + * + * do { + * res = BufferReserve(&p, buffer, size); + * if(res != ResOK) return res; // allocation fails, reason res + * initialize(p); // p now points at valid object + * } while(!BufferCommit(buffer, p, size)); + * + * Pools which do not contain formatted objects can use a one-step + * allocation as usual. Effectively any random rubbish counts as a + * "valid object" to such pools. + * + * An allocation buffer is an area of memory which is pre-allocated + * from a pool, plus a buffer descriptor, which contains, inter + * alia, four pointers: base, init, alloc, and limit. Base points + * to the base address of the area, limit to the last address plus + * one. Init points to the first uninitialized address in the + * buffer, and alloc points to the first unallocated address. + * + * L . - - - - - . ^ + * | | Higher addresses -' + * | junk | + * | | the "busy" state, after Reserve + * A |-----------| + * | uninit | + * I |-----------| + * | init | + * | | Lower addresses -. + * B `-----------' v + * + * L . - - - - - . ^ + * | | Higher addresses -' + * | junk | + * | | the "ready" state, after Commit + * A=I |-----------| + * | | + * | | + * | init | + * | | Lower addresses -. + * B `-----------' v + * + * Access to these pointers is restricted in order to allow + * synchronization between the pool and the client. The client may + * only write to init and alloc, but in a restricted and atomic way + * detailed below. The pool may read the contents of the buffer + * descriptor at _any_ time. During calls to the fill and trip + * methods, the pool may update any or all of the fields + * in the buffer descriptor. The pool may update the limit at _any_ + * time. + * + * Access to buffers by these methods is not synchronized. If a buffer + * is to be used by more than one thread then it is the client's + * responsibility to ensure exclusive access. It is recommended that + * a buffer be used by only a single thread. + * + * [Only one thread may use a buffer at once, unless the client + * places a mutual exclusion around the buffer access in the usual + * way. In such cases it is usually better to create one buffer for + * each thread.] + * + * Here are pseudo-code descriptions of the reserve and commit + * operations. These may be implemented in-line by the client. + * Note that the client is responsible for ensuring that the size + * (and therefore the alloc and init pointers) are aligned according + * to the buffer's alignment. + * + * Reserve(buf, size) ; size must be aligned to pool + * if buf->limit - buf->alloc >= size then + * buf->alloc +=size ; must be atomic update + * p = buf->init + * else + * res = BufferFill(&p, buf, size) ; buf contents may change + * + * Commit(buf, p, size) + * buf->init = buf->alloc ; must be atomic update + * if buf->limit == 0 then + * res = BufferTrip(buf, p, size) ; buf contents may change + * else + * res = True + * (returns True on successful commit) + * + * The pool must allocate the buffer descriptor and initialize it by + * calling BufferInit. The descriptor this creates will fall + * through to the fill method on the first allocation. In general, + * pools should not assign resources to the buffer until the first + * allocation, since the buffer may never be used. + * + * The pool may update the base, init, alloc, and limit fields when + * the fallback methods are called. In addition, the pool may set + * the limit to zero at any time. The effect of this is either: + * + * 1. cause the _next_ allocation in the buffer to fall through to + * the buffer fill method, and allow the buffer to be flushed + * and relocated; + * + * 2. cause the buffer trip method to be called if the client was + * between reserve and commit. + * + * A buffer may not be relocated under other circumstances because + * there is a race between updating the descriptor and the client + * allocation sequence. + + +Interface +--------- + +.. c:function:: Res BufferCreate(Buffer *bufferReturn, BufferClass class, Pool pool, Bool isMutator, ArgList args) + +:mps:tag:`method.create` Create an allocation buffer in a pool. The buffer +is created in the "ready" state. + +A buffer structure is allocated from the space control pool and +partially initialized (in particularly neither the signature nor the +serial field are initialized). The pool class's :c:func:`bufferCreate()` +method is then called. This method can update (some undefined subset +of) the fields of the structure; it should return with the buffer in +the "ready" state (or fail). The remainder of the initialization then +occurs. + +If and only if successful then a valid buffer is returned. + +.. c:function:: void BufferDestroy(Buffer buffer) + +:mps:tag:`method.destroy` Free a buffer descriptor. The buffer must be in +the "ready" state, that is, not between a Reserve and Commit. +Allocation in the area of memory to which the descriptor refers must +cease after :c:func:`BufferDestroy()` is called. + +Destroying an allocation buffer does not affect objects which have +been allocated, it just frees resources associated with the buffer +itself. + +The pool class's :c:func:`bufferDestroy()` method is called and then the +buffer structure is uninitialized and freed. + +.. c:function:: BufferCheck(Buffer buffer) + +:mps:tag:`method.check` The check method is straightforward, the non-trivial dependencies checked are: + +- The ordering constraints between base, init, alloc, and limit. +- The alignment constraints on base, init, alloc, and limit. +- That the buffer's rank is identical to the segment's rank. + +.. c:function:: void BufferAttach(Buffer buffer, Addr base, Addr limit, Addr init, Size size) + +:mps:tag:`method.attach` Set the base, init, alloc, and limit fields so that +the buffer is ready to start allocating in area of memory. The alloc +field is set to ``init + size``. + +:mps:tag:`method.attach.unbusy` :c:func:`BufferAttach()` must only be applied to +buffers that are not busy. + +.. c:function:: void BufferDetach(Buffer buffer, Pool pool) + +:mps:tag:`method.detach` Set the seg, base, init, alloc, and limit fields to +zero, so that the next reserve request will call the fill method. + +:mps:tag:`method.detach.unbusy` :c:func:`BufferDetach()` must only be applied to +buffers that are not busy. + +.. c:function:: Bool BufferIsReset(Buffer buffer) + +:mps:tag:`method.isreset` Returns :c:macro:`TRUE` if and only if the buffer is in the +reset state, that is, with base, init, alloc, and limit all set to +zero. + +.. c:function:: Bool BufferIsReady(Buffer buffer) + +:mps:tag:`method.isready` Returns :c:macro:`TRUE` if and only if the buffer is not +between a reserve and commit. The result is only reliable if the +client is not currently using the buffer, since it may update the +alloc and init pointers asynchronously. + +.. c:function:: mps_ap_t (BufferAP)(Buffer buffer) + +Returns the :c:type:`APStruct` substructure of a buffer. + +.. c:function:: Buffer BufferOfAP(mps_ap_t ap) + +:mps:tag:`method.ofap` Return the buffer which owns an :c:type:`APStruct`. + +:mps:tag:`method.ofap.thread-safe` :c:func:`BufferOfAP()` must be thread safe (see +impl.c.mpsi.thread-safety). This is achieved simply because the +underlying operation involved is simply a subtraction. + +.. c:function:: Arena BufferArena(Buffer buffer) + +:mps:tag:`method.arena` Returns the arena which owns a buffer. + +:mps:tag:`method.arena.thread-safe` :c:func:`BufferArena()` must be thread safe +(see impl.c.mpsi.thread-safety). This is achieved simple because the +underlying operation is a read of shared-non-mutable data (see +design.mps.thread-safety). + +.. c:function:: Pool BufferPool(Buffer buffer) + +Returns the pool to which a buffer is attached. + +.. c:function:: Res BufferReserve(Addr *pReturn, Buffer buffer, Size size, Bool withReservoirPermit) + +:mps:tag:`method.reserve` Reserves memory from an allocation buffer. + +This is a provided version of the reserve procedure described above. +The size must be aligned according to the buffer alignment. If +successful, ``ResOK`` is returned and ``*pReturn`` is updated with a +pointer to the reserved memory. Otherwise ``*pReturn`` is not touched. +The reserved memory is not guaranteed to have any particular contents. +The memory must be initialized with a valid object (according to the +pool to which the buffer belongs) and then passed to the +:c:func:`BufferCommit()` method (see below). ``BufferReserve(0`` may not be +applied twice to a buffer without a :c:func:`BufferCommit()` in-between. In +other words, Reserve/Commit pairs do not nest. + +.. c:function:: Res BufferFill(Addr *pReturn, Buffer buffer, Size size, Bool withReservoirPermit) + +:mps:tag:`method.fill` Refills an empty buffer. If there is not enough space +in a buffer to allocate in-line, :c:func:`BufferFill()` must be called to +"refill" the buffer. + +.. c:function:: Bool BufferCommit(Buffer buffer, Addr p, Size size) + +:mps:tag:`method.commit` Commit memory previously reserved. + +:c:func:`BufferCommit()` notifies the pool that memory which has been +previously reserved (see above) has been initialized with a valid +object (according to the pool to which the buffer belongs). The +pointer ``p`` must be the same as that returned by +:c:func:`BufferReserve()`, and the size must match the size passed to +:c:func:`BufferReserve()`. + +:c:func:`BufferCommit()` may not be applied twice to a buffer without a +reserve in between. In other words, objects must be reserved, +initialized, then committed only once. + +Commit returns :c:macro:`TRUE` if successful, :c:macro:`FALSE` otherwise. If commit +fails and returns :c:macro:`FALSE`, the client may try to allocate again by +going back to the reserve stage, and may not use the memory at ``p`` +again for any purpose. + +Some classes of pool may cause commit to fail under rare +circumstances. + +.. c:function:: BufferTrip(Buffer buffer, Addr p, Size size) + +:mps:tag:`method.trip` Act on a tripped buffer. The pool which owns a buffer +may asynchronously set the buffer limit to zero in order to get +control over the buffer. If this occurs after a :c:func:`BufferReserve()` +(but before the corresponding commit), then the :c:func:`BufferCommit()` +method calls :c:func:`BufferTrip()` and the :c:func:`BufferCommit()` method +returns with the return value of :c:func:`BufferTrip()`. + +:mps:tag:`method.trip.precondition` At the time trip is called, from +:c:func:`BufferCommit()`, the following are true: + +- :mps:tag:`method.trip.precondition.limit` ``limit == 0`` +- :mps:tag:`method.trip.precondition.init` ``init == alloc`` +- :mps:tag:`method.trip.precondition.p` ``p + size == alloc`` + + +Diagrams +-------- + +Here are a number of diagrams showing how buffers behave. In general, +the horizontal axis corresponds to mutator action (reserve, commit) +and the vertical axis corresponds to collector action. I'm not sure +which of the diagrams are the same as each other, and which are best +or most complete when they are different, but they all attempt to show +essentially the same information. It's very difficult to get all the +details in. These diagrams were drawn by Richard Brooksby, Richard +Tucker, Gavin Matthews, and others in April 1997. In general, the +later diagrams are, I suspect, more correct, complete and useful than +the earlier ones. I have put them all here for the record. Richard +Tucker, 1998-02-09. + +Buffer Diagram: +Buffer States + +Buffer States (3-column) +Buffer States (4-column) +Buffer States (gavinised) +Buffer States (interleaved) +Buffer States (richardized) + +[missing diagrams] + + diff --git a/mps/manual/html/_sources/design/cbs.txt b/mps/manual/html/_sources/design/cbs.txt deleted file mode 100644 index 0956be703a0..00000000000 --- a/mps/manual/html/_sources/design/cbs.txt +++ /dev/null @@ -1,695 +0,0 @@ -.. sources: - - ``_ - - -.. mps:prefix:: design.mps.cbs - -Coalescing block structure -========================== - - -Introduction ------------- - -:mps:tag:`intro` This is the design for :mps:ref:`impl.c.cbs`, which -implements a data structure for the management of non-intersecting -memory ranges, with eager coalescence. - -:mps:tag:`readership` This document is intended for any MM developer. - -:mps:tag:`source` :mps:ref:`design.mps.poolmv2`, :mps:ref:`design.mps.poolmvff`. - -:mps:tag:`overview` The "coalescing block structure" is a set of -addresses (or a subset of address space), with provision for efficient -management of contiguous ranges, including insertion and deletion, -high level communication with the client about the size of contiguous -ranges, and detection of protocol violations. - - -History -------- - -:mps:tag:`hist.0` This document was derived from the outline in -:mps:ref:`design.mps.poolmv2(2)`. Written by Gavin Matthews -1998-05-01. - -:mps:tag:`hist.1` Updated by Gavin Matthews 1998-07-22 in response to -approval comments in :mps:ref:`change.epcore.anchovy.160040` There is -too much fragmentation in trapping memory. - -:mps:tag:`hist.2` Updated by Gavin Matthews (as part of -:mps:ref:`change.epcore.brisling.160158`: MVFF cannot be instantiated -with 4-byte alignment) to document new alignment restrictions. - -:mps:tag:`hist.3` Converted from MMInfo database design document. -Richard Brooksby, 2002-06-07. - -:mps:tag:`hist.4` Converted to reStructuredText. Gareth Rees, -2013-04-14. - - -Definitions ------------ - -:mps:tag:`def.range` A (contiguous) range of addresses is a semi-open -interval on address space. - -:mps:tag:`def.isolated` A contiguous range is isolated with respect to -some property it has, if adjacent elements do not have that property. - -:mps:tag:`def.interesting` A block is interesting if it is of at least -the minimum interesting size specified by the client. - - -Requirements ------------- - -:mps:tag:`req.set` Must maintain a set of addresses. - -:mps:tag:`req.fast` Common operations must have a low amortized cost. - -:mps:tag:`req.add` Must be able to add address ranges to the set. - -:mps:tag:`req.remove` Must be able to remove address ranges from the set. - -:mps:tag:`req.size` Must report concisely to the client when isolated -contiguous ranges of at least a certain size appear and disappear. - -:mps:tag:`req.iterate` Must support the iteration of all isolated -contiguous ranges. This will not be a common operation. - -:mps:tag:`req.protocol` Must detect protocol violations. - -:mps:tag:`req.debug` Must support debugging of client code. - -:mps:tag:`req.small` Must have a small space overhead for the storage -of typical subsets of address space and not have abysmal overhead for -the storage of any subset of address space. - -:mps:tag:`req.align` Must support an alignment (the alignment of all -addresses specifying ranges) of down to ``sizeof(void *)`` without -losing memory. - - -Interface ---------- - -:mps:tag:`header` CBS is used through :mps:ref:`impl.h.cbs`. - - -External types -.............. - -.. c:type:: typedef struct CBSStruct CBSStruct, *CBS; - -:mps:tag:`type.cbs` :c:type:`CBS` is the main datastructure for -manipulating a CBS. It is intended that a :c:type:`CBSStruct` be -embedded in another structure. No convenience functions are provided -for the allocation or deallocation of the CBS. - -.. c:type:: typedef struct CBSBlockStruct CBSBlockStruct, *CBSBlock; - -:mps:tag:`type.cbs.block` :c:type:`CBSBlock` is the data-structure -that represents an isolated contiguous range held by the CBS. It is -returned by the new and delete methods described below. - -:mps:tag:`type.cbs.method` The following methods are provided as -callbacks to advise the client of certain events. The implementation -of these functions should not cause any CBS function to be called on -the same CBS. In this respect, the CBS module is not re-entrant. - -.. c:type:: typedef void (*CBSChangeSizeMethod)(CBS cbs, CBSBlock block, Size oldSize, SizeNewSize); - -:mps:tag:`type.cbs.change.size.method` :c:type:`CBSChangeSizeMethod` -is the function pointer type, four instances of which are optionally -registered via CBSInit. - -These callbacks are invoked under :c:func:`CBSInsert`, -:c:func:`CBSDelete`, or :c:func:`CBSSetMinSize` in certain -circumstances. Unless otherwise stated, ``oldSize`` and ``newSize`` -will both be non-zero, and different. The accessors -:c:func:`CBSBlockBase`, :c:func:`CBSBlockLimit`, and -:c:func:`CBSBlockSize` may be called from within these callbacks, -except within the delete callback when ``newSize`` is zero. See -:mps:ref:`.impl.callback` for implementation details. - -.. c:type:: typedef Bool (*CBSIterateMethod)(CBS cbs, CBSBlock block, void *closureP, unsigned long closureS); - -:mps:tag:`type.cbs.iterate.method` :c:type:`CBSIterateMethod` is a -function pointer type for a client method invoked by the CBS module -for every isolated contiguous range in address order, when passed to -the :c:func:`CBSIterate` or :c:func:`CBSIterateLarge` functions. The -function returns a boolean indicating whether to continue with the -iteration. - - -External functions -.................. - -.. c:function:: Res CBSInit(Arena arena, CBS cbs, CBSChangeSizeMethod new, CBSChangeSizeMethod delete, CBSChangeSizeMethod grow, CBSChangeSizeMethod shrink, Size minSize, Align alignment, Bool mayUseInline) - -:mps:tag:`function.cbs.init` :c:func:`CBSInit` is the function that -initialises the CBS structure. It performs allocation in the supplied -arena. Four methods are passed in as function pointers (see -:mps:ref:`.type.*` above), any of which may be ``NULL``. It receives a -minimum size, which is used when determining whether to call the -optional methods. The ``mayUseInline`` Boolean indicates whether the -CBS may use the memory in the ranges as a low-memory fallback (see -:mps:ref:`.impl.low-mem`). The alignment indicates the alignment of -ranges to be maintained. An initialised CBS contains no ranges. - -:mps:tag:`function.cbs.init.may-use-inline` If ``mayUseInline`` is -set, then ``alignment`` must be at least ``sizeof(void *)``. In this -mode, the CBS will never fail to insert or delete ranges, even if -memory for control structures becomes short. Note that, in such cases, -the CBS may defer notification of new/grow events, but will report -available blocks in :c:func:`CBSFindFirst` and :c:func:`CBSFindLast`. -Such low memory conditions will be rare and transitory. See -:mps:ref:`.align` for more details. - -.. c:function:: void CBSFinish(CBS cbs) - -:mps:tag:`function.cbs.finish` :c:func:`CBSFinish` is the function -that finishes the CBS structure and discards any other resources -associated with the CBS. - -.. c:function:: Res CBSInsert(CBS cbs, Addr base, Addr limit) - -:mps:tag:`function.cbs.insert` :c:func:`CBSInsert` is the function -used to add a contiguous range specified by ``[base,limit)`` to the -CBS. If any part of the range is already in the CBS, then -:c:macro:`ResFAIL` is returned, and the CBS is unchanged. This -function may cause allocation; if this allocation fails, and any -contingency mechanism fails, then :c:macro:`ResMEMORY` is returned, -and the CBS is unchanged. - -:mps:tag:`function.cbs.insert.callback` :c:func:`CBSInsert` will invoke callbacks as follows: - -* ``new``: when a new block is created that is interesting. ``oldSize == 0; newSize >= minSize``. - -* ``new``: when an uninteresting block coalesces to become interesting. ``0 < oldSize < minSize <= newSize``. - -* ``delete``: when two interesting blocks are coalesced. ``grow`` will also be invoked in this case on the larger of the two blocks. ``newSize == 0; oldSize >= minSize``. - -* ``grow``: when an interesting block grows in size. ``minSize <= oldSize < newSize``. - -.. c:function:: Res CBSDelete(CBS cbs, Addr base, Addr limit) - -:mps:tag:`function.cbs.delete` :c:func:`CBSDelete` is the function -used to remove a contiguous range specified by ``[base,limit)`` from -the CBS. If any part of the range is not in the CBS, then -:c:macro:`ResFAIL` is returned, and the CBS is unchanged. This -function may cause allocation; if this allocation fails, and any -contingency mechanism fails, then :c:macro:`ResMEMORY` is returned, -and the CBS is unchanged. - -:mps:tag:`function.cbs.delete.callback` :c:func:`CBSDelete` will -invoke callbacks as follows: - -* ``delete``: when an interesting block is entirely removed. ``newSize == 0; oldSize >= minSize``. -* ``delete``: when an interesting block becomes uninteresting. ``0 < newSize < minSize <= oldSize``. -* ``new``: when a block is split into two blocks, both of which are interesting. ``shrink`` will also be invoked in this case on the larger of the two blocks. ``oldSize == 0; newSize >= minSize``. -* ``shrink``: when an interesting block shrinks in size, but remains interesting. ``minSize <= newSize < oldSize``. - -.. c:function:: void CBSIterate(CBS cbs, CBSIterateMethod iterate, void *closureP, unsigned long closureS) - -:mps:tag:`function.cbs.iterate` :c:func:`CBSIterate` is the function -used to iterate all isolated contiguous ranges in a CBS. It receives a -pointer, unsigned long closure pair to pass on to the iterator method, -and an iterator method to invoke on every range in address order. If -the iterator method returns ``FALSE``, then the iteration is -terminated. - -.. c:function:: void CBSIterateLarge(CBS cbs, CBSIterateMethod iterate, void *closureP, unsigned long closureS) - -:mps:tag:`function.cbs.iterate.large` :c:func:`CBSIterateLarge` is the -function used to iterate all isolated contiguous ranges of size -greater than or equal to the client indicated minimum size in a CBS. -It receives a pointer, unsigned long closure pair to pass on to the -iterator method, and an iterator method to invoke on every large range -in address order. If the iterator method returns ``FALSE``, then the -iteration is terminated. - -.. c:function:: void CBSSetMinSize(CBS cbs, Size minSize) - -:mps:tag:`function.cbs.set.min-size` :c:func:`CBSSetMinSize` is the -function used to change the minimum size of interest in a CBS. This -minimum size is used to determine whether to invoke the client -callbacks from :c:func:`CBSInsert` and :c:func:`CBSDelete`. This -function will invoke either the ``new`` or ``delete`` callback for all -blocks that are (in the semi-open interval) between the old and new -values. ``oldSize`` and ``newSize`` will be the same in these cases. - -.. c:function:: Res CBSDescribe(CBS cbs, mps_lib_FILE *stream) - -:mps:tag:`function.cbs.describe` :c:func:`CBSDescribe` is a function -that prints a textual representation of the CBS to the given stream, -indicating the contiguous ranges in order, as well as the structure of -the underlying splay tree implementation. It is provided for debugging -purposes only. - -.. c:function:: Addr CBSBlockBase(CBSBlock block) - -:mps:tag:`function.cbs.block.base` The :c:func:`CBSBlockBase` function -returns the base of the range represented by the :c:type:`CBSBlock`. -This function may not be called from the delete callback when the -block is being deleted entirely. - -.. note:: - - The value of the base of a particular :c:type:`CBSBlock` is not - guaranteed to remain constant across calls to :c:func:`CBSDelete` - and :c:func:`CBSInsert`, regardless of whether a callback is - invoked. - -.. c:function:: Addr CBSBlockLimit(CBSBlock block) - -:mps:tag:`function.cbs.block.limit` The :c:func:`CBSBlockLimit` -function returns the limit of the range represented by the -:c:type:`CBSBlock`. This function may not be called from the delete -callback when the block is being deleted entirely. - -.. note:: - - The value of the limit of a particular :c:type:`CBSBlock` is not - guaranteed to remain constant across calls to :c:func:`CBSDelete` - and :c:func:`CBSInsert`, regardless of whether a callback is - invoked. - -.. c:function:: Size CBSBlockSize(CBSBlock block) - -:mps:tag:`function.cbs.block.size` The :c:func:`CBSBlockSize` function -returns the size of the range represented by the :c:type:`CBSBlock`. -This function may not be called from the ``delete`` callback when the -block is being deleted entirely. - -.. note:: - - The value of the size of a particular :c:type:`CBSBlock` is not - guaranteed to remain constant across calls to :c:func:`CBSDelete` - and :c:func:`CBSInsert`, regardless of whether a callback is - invoked. - -.. c:function:: Res CBSBlockDescribe(CBSBlock block, mps_lib_FILE *stream) - -:mps:tag:`function.cbs.block.describe` The :c:func:`CBSBlockDescribe` -function prints a textual representation of the :c:type:`CBSBlock` to -the given stream. It is provided for debugging purposes only. - -.. c:function:: Bool CBSFindFirst(Addr *baseReturn, Addr *limitReturn, CBS cbs, Size size, CBSFindDelete findDelete) - -:mps:tag:`function.cbs.find.first` The :c:func:`CBSFindFirst` function -locates the first block (in address order) within the CBS of at least -the specified size, and returns its range. If there are no such -blocks, it returns ``FALSE``. It optionally deletes the top, bottom, -or all of the found range, depending on the ``findDelete`` argument -(this saves a separate call to :c:func:`CBSDelete`, and uses the -knowledge of exactly where we found the range), which must come from -this enumeration:: - - enum { - CBSFindDeleteNONE, /* don't delete after finding */ - CBSFindDeleteLOW, /* delete precise size from low end */ - CBSFindDeleteHIGH, /* delete precise size from high end */ - CBSFindDeleteENTIRE /* delete entire range */ - }; - -.. c:function:: Bool CBSFindLast(Addr *baseReturn, Addr *limitReturn, CBS cbs, Size size, CBSFindDelete findDelete) - -:mps:tag:`function.cbs.find.last` The :c:func:`CBSFindLast` function -locates the last block (in address order) within the CBS of at least -the specified size, and returns its range. If there are no such -blocks, it returns ``FALSE``. Like :c:func:`CBSFindFirst`, it -optionally deletes the range. - -.. c:function:: Bool CBSFindLargest(Addr *baseReturn, Addr *limitReturn, CBS cbs, CBSFindDelete findDelete) - -:mps:tag:`function.cbs.find.largest` The :c:func:`CBSFindLargest` -function locates the largest block within the CBS, and returns its -range. If there are no blocks, it returns ``FALSE``. Like -:c:func:`CBSFindFirst`, it optionally deletes the range (specifying -``CBSFindDeleteLOW`` or ``CBSFindDeleteHIGH`` has the same effect as -``CBSFindDeleteENTIRE``). - - -Alignment ---------- - -:mps:tag:`align` When ``mayUseInline`` is specified to permit inline -data structures and hence avoid losing memory in low memory -situations, the alignments that the CBS supports are constrained by -three requirements: - -- The smallest possible range (namely one that is the alignment in - size) must be large enough to contain a single ``void *`` pointer (see - :mps:ref:`.impl.low-mem.inline.grain`); - -- Any larger range (namely one that is at least twice the alignment in - size) must be large enough to contain two ``void *`` pointers (see - :mps:ref:`.impl.low-mem.inline.block`); - -- It must be valid on all platforms to access a ``void *`` pointer - stored at the start of an aligned range. - -All alignments that meet these requirements are aligned to -``sizeof(void *)``, so we take that as the minimum alignment. - - -Implementation --------------- - -:mps:tag:`impl` Note that this section is concerned with describing -various aspects of the implementation. It does not form part of the -interface definition. - - -Size change callback protocol -............................. - -:mps:tag:`impl.callback` The size change callback protocol concerns -the mechanism for informing the client of the appearance and -disappearance of interesting ranges. The intention is that each range -has an identity (represented by the :c:type:`CBSBlock`). When blocks -are split, the larger fragment retains the identity. When blocks are -merged, the new block has the identity of the larger fragment. - -:mps:tag:`impl.callback.delete` Consider the case when the minimum -size is ``minSize``, and :c:func:`CBSDelete` is called to remove a -range of size ``middle``. The two (possibly non-existant) neighbouring -ranges have (possibly zero) sizes ``left`` and ``right``. ``middle`` is part -of the :c:type:`CBSBlock` ``middleBlock``. - -:mps:tag:`impl.callback.delete.delete` The ``delete`` callback will be -called in this case if and only if:: - - left + middle + right >= minSize && left < minSize && right < minSize - -That is, the combined range is interesting, but neither remaining -fragment is. It will be called with the following parameters: - -* ``block``: ``middleBlock`` -* ``oldSize``: ``left + middle + right`` -* ``newSize``: ``left >= right ? left : right`` - -:mps:tag:`impl.callback.delete.new` The ``new`` callback will be -called in this case if and only if:: - - left >= minSize && right >= minSize - -That is, both remaining fragments are interesting. It will be called -with the following parameters: - -* ``block``: a new block -* ``oldSize``: ``0`` -* ``newSize``: ``left >= right ? right : left`` - -:mps:tag:`impl.callback.delete.shrink` The shrink callback will be -called in this case if and only if:: - - left + middle + right >= minSize && (left >= minSize || right >= minSize) - -That is, at least one of the remaining fragments is still interesting. It will be called with the following parameters: - -* ``block``: ``middleBlock`` -* ``oldSize``: ``left + middle + right`` -* ``newSize``: ``left >= right ? left : right`` - -:mps:tag:`impl.callback.insert` Consider the case when the minimum -size is ``minSize``, and :c:func:`CBSInsert` is called to add a range -of size ``middle``. The two (possibly non-existant) neighbouring -blocks are ``leftBlock`` and ``rightBlock``, and have (possibly zero) -sizes ``left`` and ``right``. - -:mps:tag:`impl.callback.insert.delete` The ``delete`` callback will be -called in this case if and only if: - - left >= minSize && right >= minSize - -That is, both neighbours were interesting. It will be called with the -following parameters: - -* ``block``: ``left >= right ? rightBlock : leftBlock`` -* ``oldSize``: ``left >= right ? right : left`` -* ``newSize``: ``0`` - -:mps:tag:`impl.callback.insert.new` The ``new`` callback will be -called in this case if and only if: - - left + middle + right >= minSize && left < minSize && right < minSize - -That is, the combined block is interesting, but neither neighbour was. -It will be called with the following parameters: - -* ``block``: ``left >= right ? leftBlock : rightBlock`` -* ``oldSize``: ``left >= right ? left : right`` -* ``newSize``: ``left + middle + right`` - -:mps:tag:`impl.callback.insert.grow` The ``grow`` callback will be -called in this case if and only if:: - - left + middle + right >= minSize && (left >= minSize || right >= minSize) - -That is, at least one of the neighbours was interesting. It will be -called with the following parameters: - -* ``block``: ``left >= right ? leftBlock : rightBlock`` -* ``oldSize``: ``left >= right ? left : right`` -* ``newSize``: ``left + middle + right`` - - -Splay tree -.......... - -:mps:tag:`impl.splay` The CBS is principally implemented using a splay -tree (see :mps:ref:`design.mps.splay`). Each splay tree node is -embedded in a CBSBlock that represents a semi-open address range. The -key passed for comparison is the base of another range. - -:mps:tag:`impl.splay.fast-find` :c:func:`CBSFindFirst` and -:c:func:`CBSFindLast` use the update/refresh facility of splay trees -to store, in each :c:type:`CBSBlock`, an accurate summary of the -maximum block size in the tree rooted at the corresponding splay node. -This allows rapid location of the first or last suitable block, and -very rapid failure if there is no suitable block. - -:mps:tag:`impl.find-largest` :c:func:`CBSFindLargest` simply finds out -the size of the largest block in the CBS from the root of the tree -(using :c:func:`SplayRoot`), and does :c:func:`SplayFindFirst` for a -block of that size. This is O(log(*n*)) in the size of the free list, -so it's about the best you can do without maintaining a separate -priority queue, just to do :c:func:`CBSFindLargest`. Except when the -emergency lists (see :mps:ref:`.impl.low-mem`) are in use, they are -also searched. - - -Low memory behaviour -.................... - -:mps:tag:`impl.low-mem` Low memory situations cause problems when the -CBS tries to allocate a new :c:type:`CBSBlock` structure for a new -isolated range as a result of either :c:func:`CBSInsert` or -:c:func:`CBSDelete`, and there is insufficient memory to allocation -the :c:type:`CBSBlock` structure: - -:mps:tag:`impl.low-mem.no-inline` If ``mayUseInline`` is ``FALSE``, -then the range is not added to the CBS, and the call to -:c:func:`CBSInsert` or :c:func:`CBSDelete` returns ``ResMEMORY``. - -:mps:tag:`impl.low-mem.inline` If ``mayUseInline`` is ``TRUE``: - -:mps:tag:`impl.low-mem.inline.block` If the range is large enough to -contain an inline block descriptor consisting of two pointers, then it -is kept on an emergency block list. The CBS will eagerly attempt to -add this block back into the splay tree during subsequent calls to -:c:func:`CBSInsert` and :c:func:`CBSDelete`. The CBS will also keep -its emergency block list in address order, and will coalesce this list -eagerly. Some performance degradation will be seen when the emergency -block list is in use. Ranges on this emergency block list will not be -made available to the CBS's client via callbacks. :c:func:`CBSIterate` -and :c:func:`CBSIterateLarge` will not iterate over ranges on this -list. - -:mps:tag:`impl.low-mem.inline.block.structure` The two pointers stored -are to the next such block (or ``NULL``), and to the limit of the -block, in that order. - -:mps:tag:`impl.low-mem.inline.grain` Otherwise, the range must be -large enough to contain an inline grain descriptor consisting of one -pointer, then it is kept on an emergency grain list. The CBS will -eagerly attempt to add this grain back into either the splay tree or -the emergency block list during subsequent calls to -:c:func:`CBSInsert` and :c:func:`CBSDelete`. The CBS will also keep -its emergency grain list in address order. Some performance -degradation will be seen when the emergency grain list is in use. -Ranges on this emergency grain list will not be made available to the -CBS's client via callbacks. :c:func:`CBSIterate` and -:c:func:`CBSIterateLarge` will not iterate over ranges on this list. - -:mps:tag:`impl.low-mem.inline.grain.structure` The pointer stored is -to the next such grain, or ``NULL``. - - -The CBS block -............. - -:mps:tag:`impl.cbs.block` The block contains a base-limit pair and a -splay tree node. - -:mps:tag:`impl.cbs.block.special` The base and limit may be equal if -the block is halfway through being deleted. - -:mps:tag:`impl.cbs.block.special.just` This conflates values and -status, but is justified because block size is very important. - - -Testing -------- - -:mps:tag:`test` The following testing will be performed on this module: - -:mps:tag:`test.cbstest` There is a stress test for this module in -:mps:ref:`impl.c.cbstest`. This allocates a large block of memory and -then simulates the allocation and deallocation of ranges within this -block using both a :c:type:`CBS` and a :c:type:`BT`. It makes both -valid and invalid requests, and compares the :c:type:`CBS` response to -the correct behaviour as determined by the :c:type:`BT`. It also -iterates the ranges in the :c:type:`CBS`, comparing them to the -:c:type:`BT`. It also invokes the :c:func:`CBSDescribe` method, but -makes no automatic test of the resulting output. It does not currently -test the callbacks. - -:mps:tag:`test.pool` Several pools (currently :ref:`pool-mvt` and -:ref:`pool-mvff`) are implemented on top of a CBS. These pool are -subject to testing in development, QA, and are/will be heavily -exercised by customers. - - -Notes for future development ----------------------------- - -:mps:tag:`future.not-splay` The initial implementation of CBSs is -based on splay trees. It could be revised to use any other data -structure that meets the requirements (especially -:mps:ref:`.req.fast`). - -:mps:tag:`future.hybrid` It would be possible to attenuate the problem -of :mps:ref:`.risk.overhead` (below) by using a single word bit set to -represent the membership in a (possibly aligned) word-width of grains. -This might be used for block sizes less than a word-width of grains, -converting them when they reach all free in the bit set. Note that -this would make coalescence slightly less eager, by up to -``(word-width - 1)``. - - -Risks ------ - -:mps:tag:`risk.overhead` Clients should note that the current -implementation of CBSs has a space overhead proportional to the number -of isolated contiguous ranges. [Four words per range.] If the CBS -contains every other grain in an area, then the overhead will be large -compared to the size of that area. [Four words per two grains.] See -:mps:ref:`.future.hybrid` for a suggestion to solve this problem. An -alternative solution is to use CBSs only for managing long ranges. - - -Proposed hybrid implementation ------------------------------- - -.. note:: - - The following relates to a pending re-design and does not yet - relate to any working source version. GavinM 1998-09-25 - -The CBS system provides its services by combining the services -provided by three subsidiary CBS modules: - -- ``CBSST`` -- Splay Tree: Based on out-of-line splay trees; must - allocate to insert isolated, which may therefore fail. - -- ``CBSBL`` -- Block List: Based on a singly-linked list of variable - sized ranges with inline descriptors; ranges must be at least large - enough to store the inline descriptor. - -- ``CBSGL`` -- Grain List: Based on a singly-linked list of fixed size - ranges with inline descriptors; the ranges must be the alignment of - the CBS. - -The three sub-modules have a lot in common. Although their methods are -not invoked via a dispatcher, they have been given consistent -interfaces, and consistent internal appearance, to aid maintenance. - -Methods supported by sub-modules (not all sub-modules support all -methods): - -- ``MergeRange`` -- Finds any ranges in the specific CBS adjacent to - the supplied one. If there are any, it extends the ranges, possibly - deleting one of them. This cannot fail, but should return ``FALSE`` - if there is an intersection between the supplied range and a range - in the specific CBS. - -- ``InsertIsolatedRange`` -- Adds a range to the specific CBS that is - not adjacent to any range already in there. Depending on the - specific CBS, this may be able to fail for allocation reasons, in - which case it should return ``FALSE``. It should :c:func:`AVER` if - the range is adjacent to or intersects with a range already there. - -- ``RemoveAdjacentRanges`` -- Finds and removes from the specific CBS - any ranges that are adjacent to the supplied range. Should return - ``FALSE`` if the supplied range intersects with any ranges already - there. - -- ``DeleteRange`` -- Finds and deletes the supplied range from the - specific CBS. Returns a tri-state result: - - - ``Success`` -- The range was successfully deleted. This may have - involved the creation of a new range, which should be done via - ``CBSInsertIsolatedRange``. - - - ``ProtocolError`` -- Either some non-trivial strict subset of the - supplied range was in the specific CBS, or a range adjacent to the - supplied range was in the specific CBS. Either of these indicates - a protocol error. - - - ``NoIntersection`` -- The supplied range was not found in the CBS. - This may or not be a protocol error, depending on the invocation - context. - -- ``FindFirst`` -- Returns the first (in address order) range in the - specific CBS that is at least as large as the supplied size, or - ``FALSE`` if there is no such range. - -- ``FindFirstBefore`` -- As ``FindFirst``, but only finds ranges prior - to the supplied address. - -- ``FindLast`` -- As ``FindFirst``, but finds the last such range in - address order. - -- ``FindLastAfter`` -- ``FindLast`` equivalent of ``FindFirstBefore``. - -- ``Init`` -- Initialise the control structure embedded in the CBS. - -- ``Finish`` -- Finish the control structure embedded in the CBS. - -- ``InlineDescriptorSize`` -- Returns the aligned size of the inline descriptor. - -- ``Check`` -- Checks the control structure embedded in the CBS. - -The CBS supplies the following utilities: - -- ``CBSAlignment`` -- Returns the alignment of the CBS. - -- ``CBSMayUseInline`` -- Returns whether the CBS may use the memory in - the ranges stored. - -- ``CBSInsertIsolatedRange`` -- Wrapper for ``CBS*InsertIsolatedRange``. - -Internally, the ``CBS*`` sub-modules each have an internal structure -``CBS*Block`` that represents an isolated range within the module. It -supports the following methods (for sub-module internal use): - -- ``BlockBase`` -- Returns the base of the associated range; -- ``BlockLimit`` -- ``BlockRange`` -- ``BlockSize`` diff --git a/mps/manual/html/_sources/design/check.txt b/mps/manual/html/_sources/design/check.txt index 024f8b2809a..8e7a121b38b 100644 --- a/mps/manual/html/_sources/design/check.txt +++ b/mps/manual/html/_sources/design/check.txt @@ -1,6 +1,109 @@ +.. _design-check: + + .. index:: pair: checking; design -.. _design-check: -.. include:: ../../converted/check.rst +Checking +======== + +.. mps:prefix:: design.mps.check + + +Introduction +------------ + +:mps:tag:`intro` This documents the design of structure checking within the +MPS. + +:mps:tag:`readership` MPS developers. + + +Implementation +-------------- + +:mps:tag:`level` There are three levels of checking: + +1. :mps:tag:`level.sig` The lowest level checks only that the + structure has a valid ``Signature`` (see + design.mps.sig). + +2. :mps:tag:`level.shallow` Shallow checking checks all local fields + (including signature) and also checks the signatures of any parent + or child structures. + +3. :mps:tag:`level.deep` Deep checking checks all local fields + (including signatures), the signatures of any parent structures, + and does full recursive checking on any child structures. + +:mps:tag:`level.control` Control over the levels of checking is via the +definition of at most one of the macros :c:macro:`TARGET_CHECK_SHALLOW` +(which if defined gives :mps:ref:`.level.shallow`), :c:macro:`TARGET_CHECK_DEEP` +(which if defined gives :mps:ref:`.level.deep`). If neither macro is defined +then :mps:ref:`.level.sig` is used. These macros are not intended to be +manipulated directly by developers, they should use the interface in +impl.h.target. + +:mps:tag:`order` Because deep checking (:mps:ref:`.level.deep`) uses unchecked +recursion, it is important that child relationships are acyclic +(:mps:ref:`.macro.down`). + +:mps:tag:`fun` Every abstract data type which is a structure pointer should +have a function ``Check`` which takes a pointer of type +```` and returns a :c:type:`Bool`. It should check all fields in +order, using one of the macros in :mps:ref:`.macro`, or document why not. + +:mps:tag:`fun.omit` The only fields which should be omitted from a check +function are those for which there is no meaningful check (for +example, an unlimited unsigned integer with no relation to other +fields). + +:mps:tag:`fun.return` Although the function returns a :c:type:`Bool`, if the assert +handler returns (or there is no assert handler), then this is taken to +mean "ignore and continue", and the check function hence returns +:c:macro:`TRUE`. + +:mps:tag:`macro` Checking is implemented by invoking four macros in +impl.h.assert: + +.. c:function:: CHECKS(type, val) + +:mps:tag:`macro.sig` ``CHECKS(type, val)`` checks the signature only, and +should be called precisely on ``type`` and the received object +pointer. + +.. c:function:: CHECKL(cond) + +:mps:tag:`macro.local` ``CHECKL(cond)`` checks a local field (depending on +level; see :mps:ref:`.level`), and should be called on each local field that +is not an abstract data type structure pointer itself (apart from +the signature), with an appropriate normally-true test condition. + +.. c:function:: CHECKU(type, val) + +:mps:tag:`macro.up` ``CHECKU(type, val)`` checks a parent abstract data +type structure pointer, performing at most signature checks +(depending on level; see :mps:ref:`.level`). It should be called with the +parent type and pointer. + +.. c:function:: CHECKD(type, val) + +:mps:tag:`macro.down` ``CHECKD(type, val)`` checks a child abstract data +type structure pointer, possibly invoking ``Check`` (depending +on level; see :mps:ref:`.level`). It should be called with the child type +and pointer. + +:mps:tag:`full-type` :c:func:`CHECKS()`, :c:func:`CHECKD()`, :c:func:`CHECKU()`, all operate +only on fully fledged types. This means the type has to provide a +function ``Bool TypeCheck(Type type)`` where ``Type`` is substituted +for the name of the type (for example, :c:func:`PoolCheck()`), and the +expression ``obj->sig`` must be a valid value of type :c:type:`Sig` whenever +``obj`` is a valid value of type ``Type``. + +:mps:tag:`type.no-sig` This tag is to be referenced in implementations +whenever the form ``CHECKL(ThingCheck(thing))`` is used instead of +``CHECK{U,D}(Thing, thing)`` because ``Thing`` is not a fully fledged +type (:mps:ref:`.full-type`). + + diff --git a/mps/manual/html/_sources/design/class-interface.txt b/mps/manual/html/_sources/design/class-interface.txt index e7be02d9418..8e317c93e21 100644 --- a/mps/manual/html/_sources/design/class-interface.txt +++ b/mps/manual/html/_sources/design/class-interface.txt @@ -1,6 +1,261 @@ +.. _design-class-interface: + + .. index:: pair: class interface; design -.. _design-class-interface: -.. include:: ../../converted/class-interface.rst +Pool class interface +==================== + +.. mps:prefix:: design.mps.class-interface + + +Introduction +------------- + +:mps:tag:`intro` This document describes the interface and protocols between +the MPM and the pool class implementations. + +.. note:: + + This document should be merged into design.mps.pool. Pekka P. + Pirinen, 1999-07-20. + + +Methods +------- + +:mps:tag:`methods` These methods are provided by pool classes as part of the +:c:type:`PoolClass` object (see impl.h.mpmst.class). They form the interface +which allows the MPM to treat pools in a uniform manner. + +The following description is based on the definition of the +:c:type:`PoolClassStruct` (impl.h.mpmst.class). + +If a class is not required to provide a certain method then it should +set the appropriate ``PoolNo*`` method for that method. It is not +acceptable to use :c:macro:`NULL`. + +.. note:: + + There are also some ``PoolTriv*`` methods. David Jones, 1997-08-19. + +:mps:tag:`method.name` The name field should be a short, pithy, cryptic name +for the pool class. Examples are "AMC", "MV". + +The ``size`` field is the size of the pool instance structure. For the +``Foo`` :c:type:`PoolClass` this can reasonably be expected to be +``sizeof(FooStruct)``. + +The ``offset`` field is the offset into the pool instance structure of +the generic :c:type:`PoolStruct`. Typically this field is called +``poolStruct``, so something like ``offsetof(FooStruct, poolStruct)`` +is typical. If possible, arrange for this to be zero. + +The ``init`` field is the class's init method. This method is called +via the generic function :c:func:`PoolInit()`, which is in turn called by +:c:func:`PoolCreate()`. The generic function allocates the pool's structure +(using the size and offset information), initializes the +:c:type:`PoolStruct` (generic part) then calls the ``init`` method to do any +class-specific initialization. Typically this means initializing the +fields in the class instance structure. If ``init`` returns a non-OK +result code the instance structure will be deallocated and the code +returned to the caller of :c:func:`PoolInit()`` or :c:func:`PoolCreate()`. Note that +the :c:type:`PoolStruct` isn't made fully valid until :c:func:`PoolInit()` returns. + +The ``finish`` field is the class's finish method. This method is +called via the generic function :c:func:`PoolFinish()`, which is in turn +called by :c:func:`PoolDestroy()`. It is expected to finalise the pool +instance structure and release any resources allocated to the pool, it +is expected to release the memory associated with the pool instance +structure. Note that the pool is valid when it is passed to +``finish``. The :c:type:`PoolStruct` (generic part) is finished off when the +class's ``finish`` method returns. + +The ``alloc`` field is the class's allocation method. This method is +called via the generic function :c:func:`PoolAlloc()`. It is expected to +return a pointer to a fresh (that is, not overlapping with any other +live object) object of the required size. Failure to allocate should +be indicated by returning an appropriate Error code, and in such a +case, ``*pReturn`` should not be updated. Classes are not required to +provide this method, but they should provide at least one of ``alloc`` +and ``bufferCreate``. + +.. note:: + + There is no ``bufferCreate``. Gareth Rees, 2013-04-14. + +The ``free_`` field is the class's free method. This is intended +primarily for manual style pools. this method is called via the +generic function :c:func:`PoolFree()`. The parameters to this method are +required to correspond to a previous allocation request (possibly via +a buffer). It is an assertion by the client that the indicated object +is no longer required and the resources associated with it can be +recycled. Pools are not required to provide this method. + +The ``bufferInit`` field is the class's buffer initialization method. +It is called by the generic function :c:func:`BufferCreate()`, which allocates +the buffer descriptor and initializes the generic fields. The pool may +optionally adjust these fields or fill in extra values when +``bufferInit`` is called, but often pools set ``bufferInit`` to +:c:func:`PoolTrivBufferInit()` because they don't need to do any. If +``bufferInit`` returns a result code other than ``ResOK``, the buffer +structure is deallocated and the code is returned to the called of +:c:func:`BufferCreate()`. Note that the :c:type:`BufferStruct` isn't fully valid +until :c:func:`BufferCreate()` returns. + +The ``bufferFinish`` field is the class's buffer finishing method. It +is called by the the generic function :c:func:`BufferDestroy()`. The pool is +expected to detach the buffer from any memory and prepare the buffer +for destruction. The class is expected to release the resources +associated with the buffer structure, and any unreserved memory in the +buffer may be recycled. It is illegal for a buffer to be destroyed +when there are pending allocations on it (that is, an allocation has +been reserved, but not committed) and this is checked in the generic +function. This method should be provided if and only if +``bufferCreate`` is provided. [there is no ``bufferCreate`` -- drj +1997-08-19] + +The ``condemn`` field is used to condemn a pool. This method is called +via the generic function :c:func:`PoolCondemn()`. The class is expected to +condemn a subset (possible the whole set) of objects it manages and +participate in a global trace to determine liveness. The class should +register the refsig of the condemned set with the trace using +:c:func:`TraceCondemn()`. The class should expect fix requests (via the fix +method below) during a global trace. Classes are not required to +provide this method, but it is expected that automatic style classes +will. This interface is expected to change in the future. + +.. note:: + + ``condemn`` now takes an action and a segment and should condemn + the segment (turn it white) if it corresponds to the + interpretation of the action. David Jones, 1997-08-19. + + It is now called ``whiten``. David Jones, 1998-02-02. + +The ``mark`` field is used to mark an entire pool. This method is +called via the generic function :c:func:`PoolMark()`. The class should +consider all of its objects, except any set that has been condemned in +this trace, to be marked, that is ready for scanning. The class should +arrange that any appropriate invariants are preserved possibly by the +Protection interface. Classes are not required to provide this method, +and not doing so indicates that all instances of this class will have +no fixable or traceable references in them. + +.. note:: + + ``mark`` is no longer present: ``grey`` turns an entire segment + grey. David Jones, 1997-08-19. + +The ``scan`` field is used to perform scanning. This method is called +via the generic function :c:func:`PoolScan()`. The class should scan the +segment specified. It should scan all the known live (marked, that is, +those objects on which fix has been called) on the segment and +accumulate a summary of *all* the objects on the segment. This means +that mark and sweep pools may have to jump through hoops a little bit +(see design.mps.poolasm.summary for a pedagogical example). Classes +are not required to provide this method, and not doing so indicates +that all instances of this class will have no fixable or traceable +reference in them. + +.. note:: + + The ``scan`` method now takes an extra return parameter which + classes should use to indicate whether they scanned all objects in + segment or not. Classes should return summary only of object they + scanned. Caller of this method (:c:func:`TraceScan()`) is responsible + for updating summaries correctly when not a total scan. Hence no + jumping through hoops required. David Jones, 1998-01-30. + +The ``fix`` field is used to perform fixing. This method is called via +the generic function :c:func:`TraceFix()`. It indicates that the specified +reference has been found and the class should consider the object +live. There is provision for adjusting the value of the reference (to +allow for classes that move objects). Classes are not required to +provide this method, and not doing so indicates that the class is not +automatic style (ie it does not use global tracing to determine +liveness). + +The ``reclaim`` field is used to reclaim memory. This method is called +via the generic function :c:func:`PoolReclaim()`. It indicates that the trace +has fixed all references to reachable objects. + +.. note:: + + Actually it indicates that any remaining white objects have now + been proved unreachable, hence are dead. David Jones, 1997-08-19. + +The class should consider objects that have been condemned and not +fixed in this trace to be dead and may reclaim the resources +associated with them. Classes are not required to provide this method. + +.. note:: + + ``reclaim`` is now called on each segment. David Jones, + 1997-08-19. + +The ``access`` field is used to indicate client access. This method is +called via the generic functions :c:func:`SpaceAccess()` and +:c:func:`PoolAccess()`. It indicates that the client has attempted to access +the specified region, but has been denied and the request trapped due +to a protection state. The class should perform any work necessary to +remove the protection whilst still preserving appropriate invariants +(typically this will be scanning work). Classes are not required to +provide this method, and not doing so indicates they never protect any +memory managed by the pool. + +.. note:: + + ``access`` is no longer present. David Jones, 1997-08-19. + +:mps:tag:`method.act` ``act`` is called when the MPM has decided to execute +an action that the class declared. The Class should arrange execution +of the associated work (usually by beginning an incremental trace). + +:mps:tag:`method.walk` ``walk`` is used by the heap walker. ``walk`` is only +required to be implemented by classes which specify the AttrFMT +attribute (formatted pools). The ``walk`` method should apply the +passed in function (along with its closure variables (which are also +passed in) and the object format) to all *black* objects in the +segment. Padding objects may or may not be included in the walk at the +classes discretion, in any case in will be the responsibility of the +client to do something sensible with padding objects. + +.. note:: + + What about broken hearts? David Jones, 1998-01-30. + +The ``describe`` field is used to print out a description of a pool. +This method is called via the generic function :c:func:`PoolDescribe()`. The +class should emit an textual description of the pool's contents onto +the specified stream. Each line should begin with two spaces. Classes +are not required to provide this method. + + +Events +------ + +:mps:tag:`replay` To work with the allocation replayer (see +design.mps.telemetry.replayer), the pool has to emit an event for each +call to an external interface, containing all the parameters passed by +the user. If a new event type is required to carry this information, +the replayer (impl.c.eventrep) must then be extended to recreate the +call. + +:mps:tag:`replay.Init` In particular, the ``init`` method should emit a +``PoolInit`` event with all the pool parameters. + + +Text +----- + +:mps:tag:`alloc.size` The pool class implementation defines the meaning of +the "size" parameter to the ``alloc`` and ``free`` methods. It may not +actually correspond to a number of bytes of memory. + +:mps:tag:`alloc.size.align` In particular, the class may allow an unaligned +size to be passed. + + diff --git a/mps/manual/html/_sources/design/collection.txt b/mps/manual/html/_sources/design/collection.txt index ba882faa0d2..fae198acf86 100644 --- a/mps/manual/html/_sources/design/collection.txt +++ b/mps/manual/html/_sources/design/collection.txt @@ -1,6 +1,390 @@ +.. _design-collection: + + .. index:: pair: collection framework; design -.. _design-collection: -.. include:: ../../converted/collection.rst +Collection framework +==================== + +.. mps:prefix:: design.mps.collection + + +Introduction +------------ + +:mps:tag:`intro` This document describes the Collection Framework. It's a +framework for implementing garbage collection techniques and +integrating them into a system of collectors that all cooperate in +recycling garbage. + + +Overview +-------- + +:mps:tag:`framework` MPS provides a framework that allows the integration of +many different types of GC strategies and provides many of the basic +services that those strategies use. + +:mps:tag:`framework.cover` The framework subsumes most major GC strategies +and allows many efficient techniques, like in-line allocation or +software barriers. + +:mps:tag:`framework.overhead` The overhead due to cooperation is low. + +.. note:: + + But not non-existent. Can we say something useful about it? + +:mps:tag:`framework.benefits` The ability to combine collectors contributes +significantly to the flexibility of the system. The reduction in code +duplication contributes to reliability and integrity. The services of +the framework make it easier to write new MM strategies and +collectors. + +:mps:tag:`framework.mpm` The Collection Framework is merely a part of the +structure of the MPM. See design.mps.architecture and design.mps.arch +for the big picture. + +.. note:: + + Those two documents should be combined into one. Pekka P. Pirinen, + 1998-01-15. + +Other notable components that the MPM manages to integrate into a +single framework are manually-managed memory and finalization services +(see design.mps.finalize). + +.. note:: + + A document describing the design of manually-managed memory is + missing. Pekka P. Pirinen, 1998-01-15. + +:mps:tag:`see-also` This document assumes basic familiarity with the ideas +of pool (see design.mps.arch.pools) and segment (see +design.mps.seg.over). + + +Collection abstractions +----------------------- + +Colours, scanning and fixing +............................ + +:mps:tag:`state` The framework knows about the three colours of the +tri-state abstraction and free blocks. Recording the state of each +object is the responsibility of the pool, but the framework gets told +about changes in the states and keeps track of colours in each +segment. Specifically, it records whether a segment might contain +white, grey and black objects with respect to each active trace (see +:mps:ref:`.tracer`) + +.. note:: + + Black not currently implemented. Pekka P. Pirinen, 1998-01-04. + +(A segment might contain objects of all colours at once, or none.) +This information is approximate, because when an object changes +colour, or dies, it usually is too expensive to determine if it was +the last object of its former colour. + +:mps:tag:`state.transitions` The possible state transitions are as follows:: + + free ---alloc--> black (or grey) or white or none + none --condemn-> white + none --refine--> grey + grey ---scan---> black + white ----fix---> grey (or black) + black --revert--> grey + white --reclaim-> free + black --reclaim-> none + +:mps:tag:`none-is-black` Outside of a trace, objects don't really have +colour, but technically, the colour is black. Objects are only +allocated grey or white during a trace, and by the time the trace has +finished, they are either dead or black, like the other surviving +objects. We might then reuse the colour field for another trace, so +it's convenient to set the colour to black when allocating outside a +trace. This means that refining the foundation +(analysis.tracer.phase.condemn.refine), actually turns black segments +grey, rather than vice versa, but the principle is the same. + +:mps:tag:`scan-fix` "Scanning" an object means applying the "fix" function +to all references in that object. Fixing is the generic name for the +operation that takes a reference to a white object and makes it +non-white (usually grey, but black is a possibility, and so is +changing the reference as we do for weak references). Typical examples +of fix methods are copying the object into to-space or setting its +mark bit. + +:mps:tag:`cooperation` The separation of scanning and fixing is what allows +different GC techniques to cooperate. The scanning is done by a method +on the pool that the scanned object resides in, and the fixing is done +by a method on the pool that the reference points to. + +:mps:tag:`scan-all` Pools provide a method to scan all the grey objects in a +segment. + + +Reference sets +.............. + +:mps:tag:`refsets` The cost of scanning can be significantly reduced by +storing remembered sets. We have chosen a very compact and efficient +implementation, called reference sets, or refsets for short (see +idea.remember). + +.. note:: + + design.mps.refset is empty! Perhaps some of this should go there. + Pekka P. Pirinen, 1998-02-19. + +This makes the cost of maintaining them low, so we maintain them for +all references out of all scannable segments. + +:mps:tag:`refsets.approx` You might describe refsets as summaries of all +references out of an area of memory, so they are only approximations +of remembered sets. When a refset indicates that an interesting +reference might be present in a segment, we still have to scan the +segment to find it. + +:mps:tag:`refsets.scan` The refset information is collected during scanning. +The scan state protocol provides a way for the pool and the format +scan methods to cooperate in this, and to pass this information to the +tracer module which checks it and updates the segment (see +design.mps.scan). + +.. note:: + + Actually, there's very little doc there. Pekka P. Pirinen, + 1998-02-17. + +:mps:tag:`refsets.maintain` The MPS tries to maintain the refset information +when it moves or changes object. + +:mps:tag:`refsets.pollution` Ambiguous references and pointers outside the +arena will introduce spurious zones into the refsets. We put up with +this to keep the scanning costs down. Consistency checks on refsets +have to take this into account. + +:mps:tag:`refsets.write-barrier` A write-barrier are needed to keep the +mutator from invalidating the refsets when writing to a segment. We +need one on any scannable segment whose refset is not a superset of +the mutator's (and that the mutator can see). If we know what the +mutator is writing and whether it's a reference, we can just add that +reference to the refset (figuring out whether anything can be removed +from the refset is too expensive). If we don't know or if we cannot +afford to keep the barrier up, the framework can union the mutator's +refset to the segment's refset. + +:mps:tag:`refset.mutator` The mutator's refset could be computed during root +scanning in the usual way, and then kept up to date by using a +read-barrier. It's not a problem that the mutator can create new +pointers out of nothing behind the read-barrier, as they won't be real +references. However, this is probably not cost-effective, since it +would cause lots of barrier hits. We'd need a read-barrier on every +scannable segment whose refset is not a subset of the mutator's (and +that the mutator can see). So instead we approximate the mutator's +refset with the universal refset. + + +The tracer +---------- + +:mps:tag:`tracer` The tracer is an engine for implementing multiple garbage +collection processes. Each process (called a "trace") proceeds +independently of the others through five phases as described in +analysis.tracer. The following sections describe how the action of +each phase fits into the framework. See design.mps.trace for details + +.. note:: + + No, there's not much there, either. Possibly some of this section + should go there. Pekka P. Pirinen, 1998-02-18. + +:mps:tag:`combine` The tracer can also combine several traces for some +actions, like scanning a segment or a root. The methods the tracer +calls to do the work get an argument that tells them which traces they +are expected to act for. + +.. note:: + + Extend this. + +:mps:tag:`trace.begin` Traces are started by external request, usually from +a client function or an action (see design.mps.action). + +:mps:tag:`trace.progress` The tracer gets time slices from the arena to work +on a given trace. + +.. note:: + + This is just a provisional arrangement, in lieu of real progress + control. Pekka P. Pirinen, 1998-02-18. + +In each slice, it selects a small amount of work to do, based on the +state of the trace, and does it, using facilities provided by the +pools. .trace.scan: A typical unit of work is to scan a single +segment. The tracer can choose to do this for multiple traces at once, +provided the segment is grey for more than one trace. + +:mps:tag:`trace.barrier` Barrier hits might also cause a need to scan :mps:a +segment (see :mps:ref:`.hw-barriers.hit`). Again, the tracer can +:mps:choose to combine traces, when it does this. + +:mps:tag:`mutator-colour` The framework keeps track of the colour of the +mutator separately for each trace. + + +The condemn phase +................. + +:mps:tag:`phase.condemn` The agent that creates the trace (see +:mps:ref:`.trace.begin`) determines the condemned set and colours it white. +The tracer then examines the refsets on all scannable segments, and if +it can deduce some segment cannot refer to the white set, it's +immediately coloured black, otherwise the pool is asked to grey any +objects in the segment that might need to be scanned (in copying +pools, this is typically the whole segment). + +:mps:tag:`phase.condemn.zones` To get the maximum benefit from the refsets, +we try to arrange that the zones are a minimal superset (for example, +generations uniquely occupy zones) and a maximal subset (there's +nothing else in the zone) of the condemned set. This needs to be +arranged at allocation time (or when copying during collection, which +is much like allocation) + +.. note:: + + Soon, this will be handled by segment loci, see design.mps.locus. + +:mps:tag:`phase.condemn.mutator` At this point, the mutator might reference +any objects, that is, it is grey. Allocation can be in any colour, +most commonly white. + +.. note:: + + More could be said about this. + + +The grey mutator phase +...................... + +:mps:tag:`phase.grey-mutator` Grey segments are chosen according to some +sort of progress control and scanned by the pool to make them black. +Eventually, the tracer will decide to flip or it runs out of grey +segments, and proceeds to the next phase. + +.. note:: + + Currently, this phase has not been implemented; all traces flip + immediately after condemn. Pekka P. Pirinen, 1998-02-18. + +:mps:tag:`phase.grey-mutator.copy` At this stage, we don't want to copy +condemned objects, because we would need an additional barrier to keep +the mutator's view of the heap consistent (see +analysis.async-gc.copied.pointers-and-new-copy). + +:mps:tag:`phase.grey-mutator.ambig` This is a good time to get all ambiguous +scanning out of the way, because we usually can't do any after the +flip and because it doesn't cause any copying. + +.. note:: + + Write a detailed explanation of this some day. + + +The flip phase +.............. + +:mps:tag:`phase.flip` The roots (see design.mps.root) are scanned. This has +to be an atomic action as far as the mutator is concerned, so all +threads are suspended for the duration. + +:mps:tag:`phase.flip.mutator` After this, the mutator is black: if we use a +strong barrier (analysis.async-gc.strong), this means it cannot refer +to white objects. Allocation will be in black (could be grey as well, +but there's no point to it). + + +The black mutator phase +....................... + +:mps:tag:`phase.black-mutator` Grey segments are chosen according to some +sort of progress control and scanned by the pool to make them black. +Eventually, the tracer runs out of segments that are grey for this +trace, and proceeds to the next phase. + +:mps:tag:`phase.black-mutator.copy` At this stage white objects can be +relocated, because the mutator cannot see them (as long as a strong +barrier is used, as we must do for a copying collection, see +analysis.async-gc.copied.pointers). + + +The reclaim phase +................. + +:mps:tag:`phase.reclaim` The tracer finds the remaining white segments and +asks the pool to reclaim any white objects in them. + +:mps:tag:`phase.reclaim.barrier` Once a trace has started reclaiming +objects, the others shouldn't try to scan any objects that are white +for it, because they might have dangling pointers in them. + +.. note:: + + Needs cross-reference to document that is yet to be written. + + Currently, we reclaim atomically, but it could be incremental, or + even overlapped with a new trace on the same condemned set. + Pekka P. Pirinen, 1997-12-31. + + +Barriers +-------- + +.. note:: + + An introduction and a discussion of general principles should go + here. This is a completely undesigned area. + + +Hardware barriers +................. + +:mps:tag:`hw-barriers` Hardware barrier services cannot, by their very +nature, be independently provided to each trace. A segment is either +protected or not, and we have to set the protection on a segment if +any trace needs a hardware barrier on it. + +:mps:tag:`hw-barriers.supported` The framework currently supports +segment-oriented Appel-Ellis-Li barriers +(analysis.async-gc.barrier.appel-ellis-li), and write-barriers for +keeping the refsets up-to-date. It would not be hard to add Steele +barriers (analysis.async-gc.barrier.steele.scalable). + +:mps:tag:`hw-barriers.hit` When a barrier hit happens, the arena determines +which segment it was on. The segment colour info is used to determine +whether it had trace barriers on it, and if so, the appropriate +barrier action is performed, using the methods of the owning pool. If +the segment was write-protected, its refset is unioned with the refset +of the mutator. + +.. note:: In practice this is ``RefSetUNIV``. + +:mps:tag:`hw-barriers.hit.multiple` Fortunately, if we get a barrier hit on +a segment with multiple trace barriers on it, we can scan it for all +the traces that it had a barrier for. + +.. note:: Needs link to unwritten section under :mps:ref:`.combine`. + + +Software barriers +................. + +.. note:: + + Write something about software barriers. + + diff --git a/mps/manual/html/_sources/design/config.txt b/mps/manual/html/_sources/design/config.txt index 9958ebcbced..116d3616a35 100644 --- a/mps/manual/html/_sources/design/config.txt +++ b/mps/manual/html/_sources/design/config.txt @@ -1,6 +1,542 @@ +.. _design-config: + + .. index:: pair: configuration; design -.. _design-config: -.. include:: ../../converted/config.rst +MPS Configuration +================= + +.. mps:prefix:: design.mps.config + + +Introduction +------------ + +:mps:tag:`intro` This document describes how the `Memory Pool System +`_ source code is configured so +that it can target different architectures, operating systems, build +environments, varieties, and products. + + + +Requirements +------------ + +:mps:tag:`req.import` The MPS must be simple to include in third-party projects. + +:mps:tag:`req.arch` Allow architecture specific configurations of the MPS, so +that we can vary the MPS according to the target architecture. + +:mps:tag:`req.os` Allow operating system specific configurations of the MPS, +so that we can vary the MPS according to the target OS. + +:mps:tag:`req.builder` Allow build environment specific configurations of the +MPS, so that we can vary the MPS according to the compiler, etc. + +:mps:tag:`req.var` Allow configurations with different amounts of +instrumentation (assertions, metering, etc.). + +:mps:tag:`req.impact` The configuration system should have a minimal effect on +maintainability of the implementation. + +:mps:tag:`req.port` The system should be easy to port across platforms. + +:mps:tag:`req.maint` Maintenance of the configuration and build system should +not consume much developer time. + + +Retired requirements +.................... + +:mps:tag:`req.prod` Allow product specific configurations of the MPS, so that +we can build variants of the MPS for use in different products. This +requirement has been retired on 2012-09-03 as part of work on the +variety-reform_ branch. Client-specific customisation of the MPS will +be handled in source control, while the MPS source remains generic, to +reduce costs and increase reliability. See [RB_2012-09-13]_. + +.. _variety-reform: /project/mps/branch/2012-08-15/variety-reform + + +Definitions +----------- + +:mps:tag:`def.platform` A *platform* is a combination of an architecture +(:mps:ref:`.def.arch`), an operating system (:mps:ref:`.def.os`), and a builder +(:mps:ref:`.def.builder`). The set of supported platforms is maintained in the +`Platforms section of "Building the Memory Pool System" +<../manual/html/guide/build.html#platforms>`_. + +:mps:tag:`def.arch` An *architecture* is processor type with associated calling +conventions and other binary interface stuff these days often called the +`ABI `_. +Most importantly for the MPS it determines the layout of the register +file, thread context, and thread stack. + +:mps:tag:`def.os` An *operating system* is the interface to external resources. +Most importantly for the MPS it determines the low level interface to +virtual memory (if any) and threading. + +:mps:tag:`def.builder` A *builder* is the tools (C compiler, etc.) used to make +the target (:mps:ref:`.def.target`). The MPS minimises use of compiler-specific +extensions, but this is handy for suppressing warnings, inlining hints, +etc. + +:mps:tag:`def.var` A *variety* determines things like the amount of debugging, +internal consistency checking, annotation, etc. In modern IDEs this +called a "build configuration" and the usual default is to have two: +"debug" and "release". The MPS predates this convention, but the concept +is the same. + +:mps:tag:`def.prod` A *product* is the intended product into which the MPS will +fit, e.g. ScriptWorks, Dylan, etc. We no longer maintain this concept +as a dimension of configuration since :mps:ref:`.req.prod` has been retired. + +:mps:tag:`def.target` The *target* is the result of the build. + + +Overview +-------- + +:mps:tag:`import.source` The MPS can be simply included in client products as +source code. Since `version 1.110`_ we made it possible to simply +include the file ``mps.c`` in a client's build process, without +requiring a separate build of the MPS or linking a library. This is +described `section 2.3.1, "Compiling for production" of the MPS manual +<../manual/html/guide/build.html#compiling-for-production>`_. + +.. _`version 1.110`: http://www.ravenbrook.com/project/mps/version/1.110/ + +:mps:tag:`no-gen` No generated code or external tools are required. On most +platforms the only tool is the C compiler. On 64-bit Windows we require +the assembler since Microsoft withdrew in-line assembler from their C +compiler. + +:mps:tag:`no-spaghetti` Several of the MPS team have worked on some extremely +messy code bases which used a great number of ``#ifdef`` statements. +These quickly became very expensive to maintain and develop. The +general rule in the MPS is "no ``#ifdefs``". Instead, platform-specific +code is kept in separate source files and selected by carefully controlled +``#ifdefs``, such as in `mps.c <../code/mps.c>`_. + +:mps:tag:`min-dep` Dependency on a particular configuration should be +minimized and localized when developing code. This is enshrined in the +general rules for implementation [ref?] that are enforced by MPS +development procedures including code review and inspection. + + +The build system +---------------- + +Abstract build function +....................... + +:mps:tag:`build.fun` The MPS implementation assumes only a simple "build +function" that takes a set of sources, possibly in several languages, +compiles them with a set of predefined preprocessor symbols, and links +the result with a set of libraries to form the target:: + + target := build(, , ) + +:mps:tag:`build.sep` Separate compilation and linkage can be seen as a +memoization of this function, and is not strictly necessary for the +build. Indeed, since `version 1.110` we found that modern compilers +are quite happy to compile the whole MPS in one go :mps:ref:`.import.source`. + +:mps:tag:`build.cc` A consequence of this approach is that it should always +be possible to build a complete target with a single UNIX command line +calling the compiler driver (usually "cc" or "gcc"), for example:: + + cc -o main -DCONFIG_VAR_DF foo.c bar.c baz.s -lz + +:mps:tag:`build.defs` The "defs" are the set of preprocessor macros which are to be +predefined when compiling the module sources:: + + CONFIG_VAR_ + +The variety-codes are the letter code that appears after "variety." in +the tag of the relevant variety document (see variety.*) converted to +upper case. Currently (2012-09-03): + +:mps:tag:`var.hot` :c:macro:`HOT` + + Intended for release in products. Optimised, reduced internal + checking, especially on the critical path [RB_2012-09-07]_. + +:mps:tag:`var.cool` :c:macro:`COOL` + + Intended for use during development. Moderately thorough internal + consistency checking. Reduced optimisation to allow for + single-stepping. + +:mps:tag:`var.rash` :c:macro:`RASH` + + No internal checking at all. Slight performance improvement over + :mps:ref:`.var.hot` at the cost of early detection of memory management + bugs. We do not advise use of this variety, as memory management + bugs tend to be extremely expensive to deal with. + +:mps:tag:`var.diag` :c:macro:`DIAG` (deprecated) + + This variety does some client-specific analysis and produces some + specialised diagnostic output, and is not intended for general use. + It will be phased out of the open sources. + +:mps:tag:`default.hot` If no :c:macro:`CONFIG_VAR` is present, :c:macro:`HOT` is assumed in +`config.h`_. + +:mps:tag:`build.srcs` The "srcs" are the set of sources that must be +compiled in order to build the target. The set of sources may vary +depending on the configuration. For example, different sets of sources +may be required to build different architectures. + +.. note:: + + This is a dependency between the makefile (or whatever) and the + module configuration in `config.h`_. + +:mps:tag:`build.libs` The "libs" are the set of libraries to which the +compiled sources must be linked in order to build the target. For +example, when building a test program, it might include the ANSI C +library and an operating system interface library. + + +File Structure +.............. + +:mps:tag:`file.dir` The MPS source code is arranged in a single directory +called "code" containing all the sources for the whole family of +targets. + +:mps:tag:`file.base` The names of sources must be unique in the first eight +characters in order to conform to FAT filesystem naming restrictions. +(Do not scoff -- this has been an important requirement as recently as +2012!) + +:mps:tag:`file.ext` The extension may be up to three characters and directly +indicates the source language. + +:mps:tag:`file.platform` Platform-specific files include the platform code +in their name. See :mps:ref:`.mod.impls`. + + +Modules and naming +.................. + +:mps:tag:`mod.unique` Each module has an identifier which is unique within the MPS. + +:mps:tag:`mod.impls` Each module has one or more implementations which may be +in any language supported by the relevant build environment. + +:mps:tag:`mod.primary` The primary implementation of a module is written in +target-independent ANSI C in a source file with the same name as the +module. + +:mps:tag:`mod.an` Where there are platform-specific implementations and an +inferior portable ANSI C fallback implementation, "an" is used in +place of the platform code. + +:mps:tag:`mod.secondary` The names of other implementations should begin +with the same prefix (the module id or a shortened version of it) and +be suffixed with on or more target parameter codes (defined below). In +particular, the names of assembly language sources must include the +target parameter code for the relevant architecture. + +:mps:tag:`mod.example` For example, the stack scanner is defined in `ss.h +<../code/ss.h>`_ (which is platform-independent). It has some +platform-independent C in `ss.c <../code/ss.c>`_ and, for example, +`ssw3i6mv.c <../code/ssw3i6mv.c>`_ is specific to Windows on the x64 +architecture built with Microsoft Visual C. + + +Build system rationale +...................... + +:mps:tag:`build.rat` This simple design makes it possible to build the MPS +using many different tools. Microsoft Visual C and other graphical +development tools do not support much in the way of generated sources, +staged building, or other such stuff. The Visual C and Xcode "project" +files correspond closely to a closure of the build function +(:mps:ref:`.build.fun`). The simplicity of the build function has also made it +easy to set up builds using NMAKE (DOS), MPW (Macintosh), and to get the +MPS up and running on other platforms such as FreeBSD and Linux in very +little time. The cost of maintaining the build systems on these various +platforms is also reduced to a minimum, allowing the MPS developers to +concentrate on primary development. The source code is kept simple and +straightforward. When looking at MPS sources you can tell exactly what +is going to be generated with very little context. The sources are not +munged beyond the standard ANSI C preprocessor. + +:mps:tag:`build.port` The portability requirement (:mps:ref:`.req.port`) implies that +the build system must use only standard tools that will be available on +all conceivable target platforms. Experience of development +environments on the Macintosh (Metrowerks Codewarrior) and Windows NT +(Visual C++) indicates that we cannot assume much sophistication in the +use of file structure by development environments. The best that we can +hope for is the ability to combine a fixed list of source files, +libraries, and predefined preprocessor symbols into a single target. + +:mps:tag:`build.maint` The maintainability requirement (:mps:ref:`.req.maint`) implies +that we don't spend time trying to develop a set of tools to support +anything more complicated than the simple build function described +above. The effort in constructing and maintaining a portable system of +this kind is considerable. Such efforts failed in the Electronic +Publishing division of Harlequin. + + +Implementation +-------------- + +:mps:tag:`impl` The two implementation files `config.h`_ and `mpstd.h`_ can be +seen as preprocessor programs which "accept" build parameters and "emit" +configuration parameters (:mps:ref:`.fig.impl`). The build parameters are +defined either by the builder (in the case of target detection) or by +the build function (in the case of selecting the variety). + +:mps:tag:`fig.impl` + +=========================== ============== =========================================== +Build parameters Source file Configuration parameters +=========================== ============== =========================================== +:c:macro:`CONFIG_VAR_HOT` ⟶ ``config.h`` ⟶ :c:macro:`MPS_ASSERT_STRING`, etc. +``_WIN32`` ⟶ ``mpstd.h`` ⟶ :c:macro:`MPS_OS_W3`, etc. +=========================== ============== =========================================== + +:mps:tag:`impl.dep` No source code, other than the directives in `config.h`_ +and `mpstd.h`_, should depend on any build parameters. That is, +identifers beginning "CONFIG\_" should only appear in impl.h.config. +Code may depend on configuration parameters in certain, limited ways, as +defined below (:mps:ref:`.conf`). + +.. _`config.h`: <../code/config.h> +.. _`mpstd.h`: <../code/mpstd.h> + + +Target platform detection +......................... + +:mps:tag:`pf` The target platform is "detected" by the preprocessor directives in +`mpstd.h`_. + +:mps:tag:`pf.form` This file consists of sets of directives of the form:: + + #elif + #define MPS_PF_ + #define MPS_OS_ + #define MPS_ARCH_ + #define MPS_BUILD_ + #define MPS_T_WORD + #define MPS_T_ULONGEST + #define MPS_WORD_SHIFT + #define MPS_PF_ALIGN + +:mps:tag:`pf.detect` The conjunction of builder predefinitions is a constant +expression which detects the target platform. It is a logical AND of +expressions which look for preprocessor symbols defined by the build +environment to indicate the target. These must be accompanied by a +reference to the build tool documentation from which the symbols came. +For example:: + + /* "Predefined Macros" from "Visual Studio 2010" on MSDN + * . */ + + #elif defined(_MSC_VER) && defined(_WIN32) && defined(_M_IX86) + +:mps:tag:`pf.codes` The declarations of the platform, operating system, +architecture, and builder codes define preprocessor macros corresponding +the the target detected (:mps:ref:`.pf.detect`). For example:: + + #define MPS_PF_W3I3MV + #define MPS_OS_W3 + #define MPS_ARCH_I3 + #define MPS_BUILD_MV + +:mps:tag:`pf.word` The declaration of :c:macro:`MPS_T_WORD` defines the unsigned +integral type which corresponds, on the detected target, to the +machine word. It is used to defined the MPS Word type +(design.mps.type.word). For example:: + + #define MPS_T_WORD unsigned long + +We avoid using ``typedef`` here because `mpstd.h`_ could potentially +be included in assembly language source code. + +:mps:tag:`pf.word-width` The declaration of :c:macro:`MPS_WORD_WIDTH` defines the +number of bits in the type defined by :c:macro:`MPS_T_WORD` (:mps:ref:`.pf.word`) on the +target. For example:: + + #define MPS_WORD_WIDTH 32 + +:mps:tag:`pf.word-shift` The declaration of :c:macro:`MPS_WORD_SHIFT` defines the log +to the base 2 of :c:macro:`MPS_WORD_WIDTH`. For example:: + + #define MPS_WORD_SHIFT 5 + +:mps:tag:`pf.pf-align` The declaration of :c:macro:`MPS_PF_ALIGN` defines the minimum +alignment which must be used for a memory block to permit any normal +processor memory access. In other words, it is the maximum alignment +required by the processor for normal memory access. For example:: + + #define MPS_PF_ALIGN 4 + +:mps:tag:`pf.ulongest` The declaration of :c:macro:`MPS_T_ULONGEST` defines the +longest available unsigned integer type on the platform. This is +usually just ``unsigned long`` but under Microsoft C on 64-bit Windows +``unsigned long`` is just 32-bits (curse them!) For example:: + + #define MPS_T_ULONGEST unsigned __int64 + +:mps:tag:`pf.pf-string` The declaration of :c:macro:`MPS_PF_STRING` defines a string +that is used to identify the target platform in `version.c +<../code/version.c>`_. For example:: + + #define MPS_PF_STRING "w3i6mv" + + +Target varieties +................ + +:mps:tag:`var` The target variety is handled by preprocessor directives in +impl.h.config. + +:mps:tag:`var.form` The file contains sets of directives of the form:: + + #if defined(CONFIG_VAR_COOL) + #define CONFIG_ASSERT + #define CONFIG_ASSERT_ALL + #define CONFIG_STATS + +:mps:tag:`var.detect` The configured variety is one of the variety +preprocessor definitions passed to the build function +(:mps:ref:`.build.defs`), for example, :c:macro:`CONFIG_VAR_COOL`. These are +decoupled in order to keep the number of supported varieties small, +controlling each feature (for example, assertions) by a single +preprocessor definition, and maintaining flexibility about which +features are enabled in each variety. + +:mps:tag:`var.symbols` The directives should define whatever symbols are +necessary to control featrues. These symbols parameterize other parts +of the code, such as the declaration of assertions, etc. The symbols +should all begin with the prefix :c:macro:`CONFIG_`. + + +Source code configuration +------------------------- + +:mps:tag:`conf` This section describes how the configuration may affect the +source code of the MPS. + +:mps:tag:`conf.limit` The form of dependency allowed is carefully limited to +ensure that code remains maintainable and portable (:mps:ref:`.req.impact`). + +:mps:tag:`conf.min` The dependency of code on configuration parameters should +be kept to a minimum in order to keep the system maintainable +(:mps:ref:`.req.impact`). + + +Configuration Parameters +........................ + +:mps:tag:`conf.params` The compilation of a module is parameterized by:: + + MPS_ARCH_ + MPS_OS_ + MPS_BUILDER_ + MPS_PF_ + + +Abstract and Concrete Module Interfaces +....................................... + +:mps:tag:`abs.caller` Basic principle: the caller musn't be affected by +configuration of a module. This reduces complexity and dependency of +configuration. All callers use the same abstract interface. Caller +code does not change. + +:mps:tag:`abs.interface` Abstract interface includes: + +- method definitions (logical function prototypes which may be macro methods) +- names of types +- names of constants +- names of structures and fields which form part of the interface, and + possibly their types, depending on the protocol defined +- the protocols + +:mps:tag:`abs.rule` The abstract interface to a module may not be altered by a +configuration parameter. However, the concrete interface may vary. + +For example, this isn't allowed, because there is a change in the interface:: + + #if defined(PROT_FOO) + void ProtSpong(Foo foo, Bar bar); + #else + int ProtSpong(Bar bar, Foo foo); + #endif + +This example shows how:: + + #ifdef PROTECTION + void ProtSync(Space space); + /* more decls. */ + #else /* PROTECTION not */ + #define ProtSync(space) NOOP + /* more decls. */ + #endif /* PROTECTION */ + +or:: + + #if defined(PROT_FOO) + typedef struct ProtStruct { + int foo; + } ProtStruct; + #define ProtSpong(prot) X((prot)->foo) + #elif defined(PROT_BAR) + typedef struct ProtStruct { + float bar; + } ProtStruct; + #define ProtSpong(prot) Y((prot)->bar) + #else + #error "No PROT_* configured." + #endif + +Configuration parameters may not be used to vary implementations in C files. +For example, this sort of thing:: + + int map(void *base, size_t size) + { + #if defined(MPS_OS_W3) + VirtualAlloc(foo, bar, base, size); + #elif defined(MPS_OS_SU) + mmap(base, size, frob); + #else + #error "No implementation of map." + #endif + } + +This violates :mps:ref:`.no-spaghetti`. + + +To document +----------- +- What about constants in config.h? +- Update files to refer to this design document. +- Explain the role of ``mps.c`` +- Reference to ``build.txt`` +- Procedures for adding an architecture, etc. +- Reduce duplication in this document (especially after + `Configuration Parameters`_ which looks like it's been pasted in from + elsewhere.) + + +References +---------- + +.. [RB_2012-09-07] Richard Brooksby. Ravenbrook Limited. 2012-09-07. "`The critical path through the MPS `__". + +.. [RB_2012-09-13] Richard Brooksby. Ravenbrook Limited. 2013-09-13. "`The Configura CET custom mainline `__". + +.. [PP_2005-03-01] Pekka Pirinen. Global Graphics. 2005-03-01. "`MPS platforms `__". + + diff --git a/mps/manual/html/_sources/design/critical-path.txt b/mps/manual/html/_sources/design/critical-path.txt index 8bf3f0c5c44..26830c82baf 100644 --- a/mps/manual/html/_sources/design/critical-path.txt +++ b/mps/manual/html/_sources/design/critical-path.txt @@ -1,8 +1,364 @@ +.. _design-critical-path: + + .. index:: single: critical path single: path; critical single: Memory Pool System; critical path -.. _design-critical-path: -.. include:: ../../converted/critical-path.rst +The critical path through the MPS +================================= + + single: critical path + single: path; critical + single: Memory Pool System; critical path + + +Introduction +------------ +The critical path is a key concept in the design of the `Memory Pool +System `_. Code on the critical +path is usually executed more than any other code in the process. A +change of just one instruction on the critical path can make as much as +a 1% difference in overall run-time. A lot of the design of the MPS is +arranged around making the critical path as short and fast as possible. +This document describes the critical path and explains some of that +design, with reference to more detailed documents. + + +What makes the critical path critical +------------------------------------- +In order to determine which object can be recycled, the garbage +collector has to frequently examine a very large number of pointers in +the program's objects. It does this by scanning_ memory, both +allocated objects and roots (such as the thread stacks). + +This means that the scanning functions must loop over pretty much *every +word in memory* sooner or later. The MPS takes great pains to avoid +scanning memory which does not need scanning, but to get good +performance, scanning must be highly optimised. + +What's more, the scanning functions apply an operation called "fix" to +every pointer (or potential pointer) that they find in the objects in +memory. Fixing also attempts to eliminate uninteresting pointers as +fast as possible, but it has to do some work on every object that is +being considered for recycling, and that can be a large proportion of +the objects in existence. The path through fixing must also be highly +optimised, especially in the early stages. + + +How the MPS avoids scanning and fixing +-------------------------------------- +This is just a brief overview of how the MPS is designed to reduce +unnecessary scanning and fixing. + +Firstly, the MPS must occasionally decide which objects to try to +recycle. It does this using various facts it knows about the objects, +primarily their age and whether they've survived previous attempts at +recycling them. It then `"condemns"`_ a large number of objects +at once, and each of these objects must be "preserved" by fixing +references to them. + +When the MPS condemns objects it chooses sets of objects in a small set +of "zones" in memory (preferably a single zone). The zone of an object +can be determined extremely quickly from its address, without looking at +the object or any other data structure. + +The MPS arranges that objects which will probably die at the same time +are in the same zones. + +The MPS allocates in "segments". Each segment is of the order of one +"tract" of memory (generally the same as the operating system page +size, usually 4KiB or 8KiB) but may be larger if there are large +objects inside. The MPS maintains a "summary" of the zones pointed to +by all the pointers in a segment from previous scans. + +So, once the MPS has decided what to condemn, it can quickly eliminate +all segments which definitely do not point to anything in those zones. +This avoids a large amount of scanning. It is an implementation of a +`remembered set`_, though it is unlike that in most other GCs. + +In addition, the fix operation can quickly ignore pointers to the wrong +zones. This is called the "zone check" and is a BIBOP_ technique. + +Even if a pointer passes the zone check, it may still not point to a +segment containing condemned objects. The next stage of the fix +operation is to look up the segment pointed to by the pointer and see if +it was condemned. This is a fast lookup. + +After that, each pool class must decide whether the pointer is to a +condemned object and do something to preserve it. This code is still +critical. The MPS will have tried to condemn objects that are dead, but +those objects are still likely to be in segments with other objects that +must be preserved. The pool class fix method must quickly distinguish +between them. + +Furthermore, many objects will be preserved at least once in their +lifetime, so even the code that preserves an object needs to be highly +efficient. (Programs in languages like ML might not preserve 95% of +their objects even once, but many other programs will preserve nearly +all of theirs many times.) + + +Where to find the critical path +------------------------------- +Very briefly, the critical path consists of five stages: + +1. The scanner, which iterates over pointers in objects. The MPS has + several internal scanners, but the most important ones will be format + scanners in client code registered through :c:func:`mps_format_create()` + functions. + + .. note:: + + There needs to be a chapter in the manual explaining how to + write a good scanner. Then that could be linked from here. + +2. The first-stage fix, which filters out pointers inline in the + scanner. This is implemented in :c:func:`MPS_FIX()` macros in + mps.h_. + +.. _mps.h: ../code/mps.h + +3. The second-stage fix, which filters out pointers using general + information about segments. This is ``_mps_fix2`` in + `trace.c <../code/trace.c>`_. + +4. The third-stage fix, which filters out pointers using pool-specific + information. Implemented in pool class functions called :c:func:`AMCFix()`, + :c:func:`LOFix()`, etc. in pool*.c. + +5. Preserving the object, which might entail + + - marking_ it to prevent it being recycled; and/or + + - copying_ it and updating the original pointer (or just + updating the pointer, if the object has previously been + copied); and/or + + - adding it to a queue of objects to be scanned later, if it + contains pointers. + + Found in or near the pool class fix functions. + + +The format scanner +------------------ +The critical path starts when a format scan method is called. That is a +call from the MPS to a client function of type :c:type:`mps_fmt_scan_t` +registered with one of the :c:func:`mps_format_create()` functions in mps.h_. + +Here is an example of part of a format scanner for scanning contiguous +runs of pointers, from `fmtdy.c <../code/fmtdy.c>`_, the scanner for the `Open Dylan`_ +runtime:: + + static mps_res_t dylan_scan_contig(mps_ss_t mps_ss, + mps_addr_t *base, mps_addr_t *limit) + { + mps_res_t res; + mps_addr_t *p; /* reference cursor */ + mps_addr_t r; /* reference to be fixed */ + + MPS_SCAN_BEGIN(mps_ss) { + p = base; + loop: if(p >= limit) goto out; + r = *p++; + if(((mps_word_t)r&3) != 0) /* pointers tagged with 0 */ + goto loop; /* not a pointer */ + if(!MPS_FIX1(mps_ss, r)) goto loop; + res = MPS_FIX2(mps_ss, p-1); + if(res == MPS_RES_OK) goto loop; + return res; + out: assert(p == limit); + } MPS_SCAN_END(mps_ss); + + return MPS_RES_OK; + } + +(To help with understanding optimisation of this code, it's written in +a pseudo-assembler style, with one line roughly corresponding to each +instruction of an idealized intermediate code.) + +The MPS C interface provides macros to try to help optimise this code. +The ``mps_ss`` object is a "scan state" and contains data that is used +to eliminate uninteresting pointers now, and record information which +will be used to reduce scanning in future by maintaining the +remembered set. + +The macros :c:func:`MPS_SCAN_BEGIN()` and :c:func:`MPS_SCAN_END()` load key data +from the scan state into local variables, and hopefully into processor +registers. This avoids aliasing values that we know won't change when +calls are made to ``_mps_fix2`` later, and so allows the compiler to +keep the scan loop small and avoid unnecessary memory references. + +This scanner knows that words not ending in 0b00 aren't pointers to +objects, so it eliminates them straight away. This is a kind of +`reference tag`_ chosen by the client for its object representation. + +Next, the pointer is tested using :c:func:`MPS_FIX1()`. This performs fast +tests on the pointer without using any other memory. In particular, it +does the "zone check" described in section 3. If a pointer fails these +tests, it isn't interesting and can be skipped. It is very important +to proceed to the next pointer as fast as possible in this case. + +Having passed these tests, we need to fix the pointer using other data +in memory, and possibly call the MPS to preserve the object. This is +what :c:func:`MPS_FIX2()` does. The important distinction here is that +:c:func:`MPS_FIX2()` can fail and return an error code, which must be +propagated without ado by returning from the scanner. Separating +:c:func:`MPS_FIX1()` from :c:func:`MPS_FIX2()` helps keep the error handling code +away from the tight loop with the zone check. + +``MPS_FIX*``, the macro/inline part of the fix operation, are referred +to as "fix stage 1" or "the first stage fix" in other documents and +comments. + +If these inline checks pass, ``_mps_fix2`` is called. If the MPS has +been built as a separate object file or library, this is where the +function call out of the scan loop happens. Since version 1.110 of the +MPS, we encourage clients to compile the MPS in the same translation +unit as their format code, so that the compiler can be intelligent +about inlining parts of ``_mps_fix2`` in the format scanner. The +instructions for doing this are in `Building the Memory Pool System +<../manual/build.txt>`_, part of the manual. + + +The second stage fix in the MPM +------------------------------- +If a pointer gets past the first-stage fix filters, it is passed to +``_mps_fix2``, the "second stage fix". The second stage can filter out +yet more pointers using information about segments before it has to +consult the pool class. + +The first test applied is the "tract test". The MPS looks up the tract +containing the address in the tract table, which is a simple linear +table indexed by the address shifted -- a kind of flat page table. + +Note that if the arena has been extended, the tract table becomes less +simple, and this test may involved looking in more than one table. +This will cause a considerable slow-down in garbage collection +scanning. This is the reason that it's important to give a good +estimate of the amount of address space you will ever occupy with +objects when you initialize the arena. + +The pointer might not even be in the arena (and so not in any tract). +The first stage fix doesn't guarantee it. So we eliminate any pointers +not in the arena at this stage. + +If the pointer is in an allocated tract, then the table also contains +a cache of the "white set" -- the set of garbage collection traces for +which the tract is "interesting". If a tract isn't interesting, then +we know that it contains no condemned objects, and we can filter out +the pointer. + +If the tract is interesting them it's part of a segment containing +objects that have been condemned. The MPM can't know anything about +the internal layout of the segment, so at this point we dispatch to +the third stage fix. + +This dispatch is slightly subtle. We have a cache of the function to +dispatch to in the scan state, which has recently been looked at and +is with luck still in the processor cache. The reason there is a +dispatch at all is to allow for a fast changeover to emergency garbage +collection, or overriding of garbage collection with extra operations. +Those are beyond the scope of this document. Normally, ``ss->fix`` +points at :c:func:`PoolFix()`, and we rely somewhat on modern processor +`branch target prediction +`_). +:c:func:`PoolFix()` is passed the pool, which is fetched from the tract +table entry, and that should be in the cache. + +:c:func:`PoolFix()` itself dispatches to the pool class. Normally, a +dispatch to a pool class would indirect through the pool class object. +That would be a double indirection from the tract, so instead we have +a cache of the pool's fix method in the pool object. This also allows +a pool class to vary its fix method per pool instance, a fact that is +exploited to optimize fixing in the AMC Pool depending on what kind of +object format it is managing. + + +The third stage fix in the pool class +------------------------------------- +The final stage of fixing is entirely dependent on the pool class. The +MPM can't, in general, know how the objects within a pool are arranged, +so this is pool class specific code. + +Furthermore, the pool class must make decisions based on the "reference +rank" of the pointer. If a pointer is ambiguous (``RankAMBIG``) then it +can't be changed, so even a copying pool class can't move an object. +On the other hand, if the pointer is weak (``RankWEAK``) then the pool fix +method shouldn't preserve the object at all, even if it's condemned. + +The exact details of the logic that the pool fix must implement in +order to co-operate with the MPM and other pools are beyond the scope +of this document, which is about the critical path. Since it is on +the critical path, it's important that whatever the pool fix does is +simple and fast and returns to scanning as soon as possible. + +The first step, though, is to further filter out pointers which aren't +to objects, if that's its policy. Then, it may preserve the object, +according to its policy, and possibly ensure that the object gets +scanned at some point in the future, if it contains more pointers. + +If the object is moved to preserve it (for instance, if the pool class +implements a copying GC), or was already moved when fixing a previous +reference to it, the reference being fixed must be updated (this is +the origin of the term "fix"). + +As a simple example, :c:func:`LOFix()` is the pool fix method for the Leaf +Only pool class. It implements a marking garbage collector, and does +not have to worry about scanning preserved objects because it is used +to store objects that don't contain pointers. (It is used in compiler +run-time systems to store binary data such as character strings, thus +avoiding any scanning, decoding, or remembered set overhead for them.) + +:c:func:`LOFix()` filters any ambiguous pointers that aren't aligned, since +they can't point to objects it allocated. Otherwise it subtracts the +segment base address and shifts the result to get an index into a mark +bit table. If the object wasn't marked and the pointer is weak, then +it sets the pointer to zero, since the object is about to be recycled. +Otherwise, the mark bit is set, which preserves the object from +recycling when `LOReclaim` is called later on. `LOFix` illustrates +about the minimum and most efficient thing a pool fix method can do. + + +Other considerations +-------------------- +So far this document has described the ways in which the garbage +collector is designed around optimising the critical path. There are a +few other things that the MPS does that are important. + +Firstly, inlining is very important. The first stage fix is inlined +into the format scanner by being implemented in macros in +mps.h_. And to get even better inlining, `we +recommend <../manual/build.txt>`_ that the whole MPS is compiled in a +single translation unit with the client format and that strong global +optimisation is applied. + +Secondly, we are very careful with code annotations on the critical +path. Assertions, statistics, and telemetry are all disabled on the +critical path in "hot" (production) builds. (In fact, it's because the +critical path is critical that we can afford to leave annotations +switched on elsewhere.) + +Last, but by no means least, we pay a lot of brainpower and measurement +to the critical path, and are very very careful about changing it. Code +review around the critical path is especially vigilant. + +And we write long documents about it. + + +References +---------- +.. [MMRef] "The Memory Management Reference"; . +.. _scanning: http://www.memorymanagement.org/glossary/s.html#scan +.. _marking: http://www.memorymanagement.org/glossary/m.html#marking +.. _copying: http://www.memorymanagement.org/glossary/c.html#copying.garbage.collection +.. _`"condemns"`: http://www.memorymanagement.org/glossary/c.html#condemned.set +.. _BIBOP: http://www.memorymanagement.org/glossary/b.html#bibop +.. _`remembered set`: http://www.memorymanagement.org/glossary/r.html#remembered.set +.. _`reference tag`: http://www.memorymanagement.org/glossary/t.html#tag +.. _`Open Dylan`: http://opendylan.org/ + + diff --git a/mps/manual/html/_sources/design/diag.txt b/mps/manual/html/_sources/design/diag.txt new file mode 100644 index 00000000000..4c8a16a1137 --- /dev/null +++ b/mps/manual/html/_sources/design/diag.txt @@ -0,0 +1,290 @@ +.. _design-diag: + + +.. index:: + pair: diagnostic feedback; design + + +Diagnostic feedback +=================== + +.. mps:prefix:: design.mps.diag + + +Introduction +------------ + +:mps:tag:`intro` This document describes how to use the diagnostic feedback +mechanism in the Memory Pool System. + +:mps:tag:`sources` Initially abased on [RHSK_2007-04-13]_ and [RHSK_2007-04-18]_. + + +Overview +-------- + +Diagnostic feedback is information created by the MPS diagnostic +system for the purpose of helping MPS programmers client-code +programmers. + +Such a piece of information is called "a diagnostic". (See also +:mps:ref:`.parts`.) + +A diagnostic is not intended to be end-user readable (or visible), or +machine-parseable. + +A diagnostic is not intended to be stable from one release to the +next: it may be modified or removed at any time. + + +Requirements +------------ + +MPS diagnostic feedback code must do these things: + +- calculate, store, and propagate data; +- collate, synthesise, and format it into a human-useful diagnostic; +- control (for example, filter) output of diagnostics; +- use a channel to get the diagnostic out. + +Note: the knowledge/code/logic for constructing the human-useful +message is kept inside normal MPS source code. This means it is always +in-sync with changes to the MPS. This also means that any external +utilities used to display the messages do not need to understand, or +keep in sync with, the details of what's going inside the MPS. + + +Usage +----- + +To run the MPS and get diagnostic output from it: + +1. Use a variety with diagnostics compiled-in. Currently, that means + variety.di. See ``config.h``. + +2. Check that the diagnostics you require are generated, by looking in + MPS source for invocations of the appropriate macro (for example, + :c:func:`DIAG_SINGLEF()`). + +3. Check that the diagnostics you require will be output, by looking + at the diagnostic filter rules in ``diag.c``. + +4. Run the MPS and client in an environment that supports the channel + used (for example, at a command-line if using :c:func:`WriteF()`). + + +What is a diagnostic? +..................... + +A diagnostic has three parts: + +1. a trigger condition, that causes this diagnostic to be emitted; +2. a text tag (for example, "TraceStart") which is the name of this + diagnostic; and +3. a paragraph of human-useful text. + +A diagnostic is emitted by the MPS at a certain point in time when a +certain event happens. + +Diagnostics are not nested. Every diagnostic must have a tag. Each +diagnostic should have a unique tag (uniqueness is just to help the +humans; the diagnostic system does not care). + +The paragraph of text can be many lines long. It usually explains what +event caused the diagnostic to be emitted, and commonly also includes +the output of some :c:func:`Describe()` methods for various relevant +objects. (For example, the ``TraceStart`` diagnostic might call, and +include the output generated by, the :c:func:`TraceDescribe()` method). + +How do I control (filter) which diagnostics I see? +.................................................. + +All diagnostics are emitted and then filtered according to the +"diagnostic filter rules". + +The first level of control is filtering by tag. (For example, only +show ``TraceStart`` diagnostics). + +The second level of control is filtering by paragraph content. (For +example, only show ``TraceStart`` diagnostics where the trace is +started because a nursery generation is full). + +The third level of control is filtering by line content. (For example, +only show lines containing the word ``whiteSet``). + +See ``diag.c`` for details. + +Note: the entire filtering mechanism can be turned off, so that +diagnostics go immediately to ``mps_lib_get_stdout(0``, with no +buffering or filtering See impl.c.diag.filter-disable. + + +How to write a diagnostic +------------------------- + +Improve stateless Describe methods where possible +................................................. + +Where possible, don't put clever code into an event-triggered +diagnostic: put it into a stateless :c:func:`Describe()` method instead, and +then call that method when emitting your diagnostic. + +For example:: + + FooDescribe(Foo foo, mps_lib_FILE *stream) + { + /* show value of new "quux" field */ + WriteF(stream, "Foo: $P { quux: $U }\n", foo, foo->quux); + } + + FooWibble(Foo foo) + { + ... + DIAG_FIRSTF(( "FooWibble", "Wibbling foo $P", foo, NULL)); + DIAG( FooDescribe(foo, DIAG_STREAM); ); + DIAG_END("FooWibble"); + ... + } + +This is much better, because other people can use your human-useful +output in their diagnostics, or 'live' in a debugger. + + +Use the output macros +..................... + +For a simple diagnostic, use :c:func:`DIAG_SINGLEF()`. This begins the tag, +puts text into the paragraph, and ends the tag immediately. + +For a more complex diagnostic, the first call must be +:c:func:`DIAG_FIRSTF()`, which begins a diag tag. + +While a tag is current, you can add text to the diagnostic's paragraph +using :c:func:`DIAG_MOREF()`, and ``WriteF( DIAG_STREAM, ... )``. + +.. note:: + + :c:macro:`DIAG_STREAM` is not a real standard C library stream. If you + want stream-level access, you may use :c:func:`Stream_fputc()` and + :c:func:`Stream_fputs()`. + +End the tag by calling :c:macro:`DIAG_END`. + + +Compile away in non-diag varieties; no side effects +................................................... + +Wrap non-output code with the :c:func:`DIAG()` and :c:func:`DIAG_DECL()` macros, +to make sure that non-diag varieties do not execute +diagnostic-generating code. + +For complex diagnostic-generating code, it may be cleaner to move it +into a separate local function. Put ``_diag`` on the end of the function +name (for example, :c:func:`TraceStart_diag()`). + +Obviously, diagnostic-generating code must have no side effects. + + +Choosing tags +............. + +Tags should be valid C identifiers. Unless you know of a good reason +why not. (Not currently checked). + +There's no formal scheme for tag naming, but make it helpful and +informally hierarchical, for example, ``TraceBegin``, ``TraceStart``, +``TraceEnd``, and so on, not ``BeginTrace``, ``EndTrace``. + + +Writing good paragraph text +........................... + +IMPORTANT: Make your diagnostics easy to understand! Other people will +read your diagnostics! Make them clear and helpful. Do not make them +terse and cryptic. If you use symbols, print a key in the diagnostic. +(If you don't want to see this the screen clutter, then you can always +add a filter rule to your personal rule set to filter it out). + + +Maintaining helpful filter rules +................................ + +If you add a noisy diagnostic, add a rule to the default ruleset to +turn it off. + + +How the MPS diagnostic system works +----------------------------------- + +Channels +........ + +The recommended channel is :c:func:`WriteF()` to standard output. + +Other possible of future channels might be: + +- :c:func:`printf()`; +- a new type (yet to be defined) of ``mps_message``; +- squirt them into the telemetry-log-events system; +- telnet. + +Currently, only :c:func:`printf()` and :c:func:`WriteF()` are supported. See the +:c:macro:`DIAG_WITH_` macros in ``mpm.h``. + +You can also use a debugger to call :c:func:`Describe()` methods directly, +from within the debugger. + +Note: it is unfortunate that choice of channel may (for some channels) +also dictate the form of the code that synthesises the message. (For +example, :c:func:`WriteF()` style parameter-expansion is not possible when +using the :c:func:`printf()` channel, because there is no way to get +:c:func:`WriteF()` to produce its output into a string). This is just a +technical hitch; logically, the code that synthesises a diagnostic +message should not care which channel will be used to transmit it out +of the MPS. + + +Parts of the MPS diagnostic system +.................................. + +:mps:tag:`parts` The following facilities are considered part of the MPS +diagnostic system: + +- the :c:func:`Describe()` methods. +- the :c:macro:`DIAG` macros (:c:macro:`DIAG`, :c:macro:`DIAG_DECL`, ``DIAG_*F``, and so on); +- the :c:macro:`STATISTIC` macros (see ``mpm.h``); +- the :c:macro:`METER` macros and meter subsystem. + + +Related systems +............... + +The MPS diagnostic system is separate from the following other MPS +systems: + +- The telemetry-log-events system. This emits much more data, in a + less human-readable form, requires MPS-aware external tools, and is + more stable from release to release). In non-diagnostic telemetry + varieties, the telemetry-log-events system emits events that log all + normal MPS actions. In diagnostic telemetry varieties, it may emit + additional events containing diagnostic information. Additionally, + the telemetry-log-events stream might in future be available as a + channel for emitting human-readable text diagnostics. See also + design.mps.telemetry. + +- The MPS message system. This is present in all varieties, and + manages asynchronous communication from the MPS to the client + program). However, the MPS message system might in future also be + available as a channel for emitting diagnostics. See also + design.mps.message. + + + +References +---------- + +.. [RHSK_2007-04-13] Richard Kistruck. 2007-04-13. "`diagnostic feedback from the MPS `_". + +.. [RHSK_2007-04-18] Richard Kistruck. 2007-04-18. "`Diverse types of diagnostic feedback `_". + + diff --git a/mps/manual/html/_sources/design/finalize.txt b/mps/manual/html/_sources/design/finalize.txt index 9cd0521e392..01fbc83f1ab 100644 --- a/mps/manual/html/_sources/design/finalize.txt +++ b/mps/manual/html/_sources/design/finalize.txt @@ -1,6 +1,120 @@ +.. _design-finalize: + + .. index:: pair: finalization; design -.. _design-finalize: -.. include:: ../../converted/finalize.rst +Finalization +============ + +.. mps:prefix:: design.mps.finalize + + +Overview +-------- + +:mps:tag:`overview` Finalization is implemented internally using the +Guardian Pool Class (design.mps.poolmrg). Objects can be registered +for finalization using an interface function (called +:c:func:`mps_finalize()`). Notification of finalization is given to the client +via the messaging interface. ``PoolClassMRG`` (design.mps.poolmrg) +implements a Message Class which implements the finalization messages. + + +Requirements +------------ + +:mps:tag:`req` Historically only Dylan had requirements for finalization, +see req.dylan.fun.final. Now (2003-02-19) Configura have requirements +for finalization. Happily they are very similar. + + +Architecture +------------ + +External interface +.................. + +:mps:tag:`if.register` :c:func:`mps_finalize()` increases the number of times that +an object has been registered for finalization by one. The object must +have been allocated from the arena (space). Any finalization messages +that are created for this object will appear on the arena's message +queue. The MPS will attempt to finalize the object that number of +times. + +:mps:tag:`if.deregister` :c:func:`mps_definalize()` reduces the number of times that +the object located at ``obj`` has been registered for finalization by +one. It is an error to definalize an object that has not been +registered for finalization. + +:mps:tag:`if.deregister.not` At the moment (1997-08-20) :c:func:`mps_definalize()` +is not implemented. + +:mps:tag:`if.get-ref` :c:func:`mps_message_finalization_ref()` returns the reference +to the finalized object stored in the finalization message. + + +Implementation +-------------- + +:mps:tag:`int.over` Registering an object for finalization corresponds to +allocating a reference of rank FINAL to that object. This reference is +allocated in a guardian object in a pool of ``PoolClassMRG`` (see +design.mps.poolmrg). + +:mps:tag:`int.arena.struct` The MRG pool used for managing final references +is kept in the Arena (Space), referred to as the "final pool". + +:mps:tag:`int.arena.lazy` The pool is lazily created. It will not be created +until the first object is registered for finalization. + +:mps:tag:`int.arena.flag` There is a flag in the Arena that indicates +whether the final pool has been created yet or not. + +.. c:function:: Res ArenaFinalize(Arena arena, Ref addr) + +:mps:tag:`int.finalize.create` Creates the final pool if it has not been +created yet. + +:mps:tag:`int.finalize.alloc` Allocates a guardian in the final pool. + +:mps:tag:`int.finalize.write` Writes a reference to the object into the +guardian object. + +:mps:tag:`int.finalize.all` That's all. + +:mps:tag:`int.finalize.error` If either the creation of the pool or the +allocation of the object fails then the error will be reported back to +the caller. + +:mps:tag:`int.finalize.error.no-unwind` This function does not need to do +any unwinding in the error cases because the creation of the pool is +not something that needs to be undone. + +:mps:tag:`int.arena-destroy.empty` :c:func:`ArenaDestroy()` empties the message +queue by calling :c:func:`MessageEmpty()`. + +:mps:tag:`int.arena-destroy.final-pool` If the final pool has been created +then :c:func:`ArenaDestroy()` destroys the final pool. + +:mps:tag:`access` :c:func:`mps_message_finalization_ref()` needs to access the +finalization message to retrieve the reference and then write it to +where the client asks. This must be done carefully, in order to avoid +breaking the invariants or creating a hidden root. + +:mps:tag:`access.invariants` We protect the invariants by using special +routines :c:func:`ArenaRead()` and :c:func:`ArenaPoke()` to read and write the +reference. This works as long as there's no write-barrier collection. + +.. note:: + + Instead of :c:func:`ArenaPoke()`, we could put in an :c:func:`ArenaWrite()` + that would be identical to :c:func:`ArenaPoke()`, except that it would + :c:func:`AVER()` the invariant (or it can just :c:func:`AVER()` that there are + no busy traces unflipped). When we get write-barrier collection, + we could change it to do the real thing, but in the absence of a + write-barrier, it's functionally identical to :c:func:`ArenaPoke()`. + Pekka P. Pirinen, 1997-12-09. + + diff --git a/mps/manual/html/_sources/design/fix.txt b/mps/manual/html/_sources/design/fix.txt index 2d11b05a35f..9b2b059bc0a 100644 --- a/mps/manual/html/_sources/design/fix.txt +++ b/mps/manual/html/_sources/design/fix.txt @@ -1,6 +1,56 @@ +.. _design-fix: + + .. index:: pair: fix function; design -.. _design-fix: -.. include:: ../../converted/fix.rst +The generic fix function +======================== + +.. mps:prefix:: design.mps.fix + + +Introduction +------------- + +:mps:tag:`intro` Fix is the interface through which the existence of +references are communicated from the MPS client to the MPS. The +interface also allows the value of such references to be changed (this +is necessary in order to implement a moving memory manager). + + +Architecture +------------- + +:mps:tag:`protocol.was-marked` The :c:type:`ScanState` has a :c:type:`Bool` +``wasMarked`` field. This is used for finalization. + +:mps:tag:`protocol.was-marked.set` All pool-specific fix methods must set +the ``wasMarked`` field in the :c:type:`ScanState` that they are passed. + +:mps:tag:`protocol.was-marked.meaning` If the pool-specific fix method sets +the ``wasMarked`` field to :c:macro:`FALSE` it is indicating the object +referred to by the ref (the one that it is supposed to be fixing) has +not previously been marked (ie, this is the first reference to this +object that has been fixed), and that the object was white (in +condemned space). + +:mps:tag:`protocol.was-marked.conservative` It is always okay to set the +``wasMarked`` field to :c:macro:`TRUE`. + +:mps:tag:`protocol.was-marked.finalizable` The MRG pool (design.mps.poolmrg) +uses the value of the ``wasMarked`` field to determine whether an +object is finalizable. + + +Implementation +--------------- + +:mps:tag:`fix.nailed` In a copying collection, a non-ambiguous fix to a +broken heart should be snapped out *even if* there is a ``RankAMBIG`` +ref to same object (that is, if the broken heart is nailed); the +``RankAMBIG`` reference must either be stale (no longer in existence) +or bogus. + + diff --git a/mps/manual/html/_sources/design/guide.hex.trans.txt b/mps/manual/html/_sources/design/guide.hex.trans.txt index 3c877c5ad08..5f31fb48d26 100644 --- a/mps/manual/html/_sources/design/guide.hex.trans.txt +++ b/mps/manual/html/_sources/design/guide.hex.trans.txt @@ -1,6 +1,102 @@ +.. _design-guide.hex.trans: + + .. index:: pair: hexadecimal; transliterating -.. _design-guide.hex.trans: -.. include:: ../../converted/guide.hex.trans.rst +Transliterating the alphabet into hexadecimal +============================================= + +.. mps:prefix:: guide.hex.trans + + +Introduction +------------ + +:mps:tag:`scope` This document explains how to represent the alphabet as +hexadecimal digits. + +:mps:tag:`readership` This document is intended for anyone devising +arbitrary constants which may appear in hex-dumps. + +:mps:tag:`sources` This transliteration was supplied by RichardK in +mail.richardk.1997-04-07.13-44. + + +Transliteration +--------------- + +:mps:tag:`forward` The chosen transliteration is as follows:: + + ABCDEFGHIJKLMNOPQRSTUVWXYZ + ABCDEF9811C7340BC6520F3812 + +:mps:tag:`backward` The backwards transliteration is as follows:: + + 0 OU + 1 IJY + 2 TZ + 3 MW + 4 N + 5 S + 6 R + 7 L + 8 HX + 9 G + A A + B BP + C CKQ + D D + E E + F FV + +:mps:tag:`pad` If padding is required (to fill a hex constant length), you +should use 9's, because G is rare and can usually be inferred from +context. + +:mps:tag:`punc` There is no formal scheme for spaces, or punctuation. It is +suggested that you use 9 (as :mps:ref:`.pad`). + + +Justification +-------------- + +:mps:tag:`letters` The hexadecimal letters (A-F) are all formed by +similarity of sound. B and P sound similar, as do F and V, and C, K, & +Q can all sound similar. + +:mps:tag:`numbers` The numbers (0-9) are all formed by similarity of shape +(but see :mps:ref:`.trans.t`). Nevertheless, 1=IJY retains some similarity of +sound. + +:mps:tag:`trans.t` T is an exception to :mps:ref:`.numbers`, but is such a common +letter that it deserves it. + + +Notes +----- + +:mps:tag:`change` This transliteration differs from the old transliteration +used for signatures (see design.mps.sig(0)), as follows: J:6->1; +L:1->7; N:9->4; R:4->6; W:8->3; X:5->8; Y:E->I. + +:mps:tag:`problem.mw` There is a known problem that M and W are both common, +map to the same digit (3), and are hard to distinguish in context. + +:mps:tag:`find.c` It is possible to find all 8-digit hexadecimal constants +and how many times they're used in C files, using the following Perl +script:: + + perl5 -n -e 'BEGIN { %C=(); } if(/0x([0-9A-Fa-f]{8})/) { $C{$1} = +[] if( + !defined($C{$1})); push(@{$C{$1}}, $ARGV); } END { foreach $H (sort(keys(%C))) + { printf "%3d %s %s\n", scalar(@{$C{$H}}), $H, join(", ", @{@C{$H}}); } }' *.c + *.h + +:mps:tag:`comment` It is a good idea to add a comment to any constant +declaration indicating the English version and which letters were +selected (by capitalisation), e.g.:: + + #define SpaceSig ((Sig)0x5195BACE) /* SIGnature SPACE */ + + diff --git a/mps/manual/html/_sources/design/guide.impl.c.format.txt b/mps/manual/html/_sources/design/guide.impl.c.format.txt new file mode 100644 index 00000000000..89dad45e73b --- /dev/null +++ b/mps/manual/html/_sources/design/guide.impl.c.format.txt @@ -0,0 +1,427 @@ +.. _design-guide.impl.c.format: + + +.. index:: + pair: C language; formatting guide + pair: C language formatting; guide + + +C Style -- formatting +===================== + +.. mps:prefix:: guide.impl.c.format + pair: C language; formatting guide + pair: C language formatting; guide + + +Introduction +------------ + +:mps:tag:`scope` This document describes the Ravenbrook conventions for the +general format of C source code in the MPS. + +:mps:tag:`readership` This document is intended for anyone working on or with the +C source code. + + +General Formatting Conventions +------------------------------ + +Line Width +.......... + +:mps:tag:`width` Lines should be no wider than 72 characters. :mps:tag:`width.why` Many +people use 80 column terminal windows so that multiple windows can be +placed side by side. Restricting lines to 72 characters allows line +numbering to be used (in vi for example) and also allows diffs to be +displayed without overflowing the terminal. + +White Space +........... + +:mps:tag:`space.notab` No tab characters should appear in the source files. +Ordinary spaces should be used to indent and format the sources. +:mps:tag:`space.notab.why` Tab characters are displayed differently on different +platforms, and sometimes translated back and forth, destroying layout +information. + +:mps:tag:`space.punct` There should always be whitespace after commas and +semicolons and similar punctuation. + +:mps:tag:`space.op` Put white space around operators in expressions, except when +removing it would make the expression clearer by binding certain +sub-expressions more tightly. For example:: + + foo = x + y*z; + +:mps:tag:`space.control` One space between the keyword, ``switch``, ``while``, +``for`` and the following paren. :mps:tag:`space.control.why` This distinguishes +control statements lexically from function calls, making it easier to +distinguish them visually and when searching with tools like grep. + +:mps:tag:`space.function.not` No space between a function name and the following +paren beginning its argument list. + +Sections and Paragraphs +....................... + +:mps:tag:`section` Source files can be thought of as breaking down into +"sections" and "paragraphs". A section might be the leader comment of a +file, the imports, or a set of declarations which are related. + +:mps:tag:`section.space` Precede sections by two blank lines (except the first +one in the file, which should be the leader comment in any case). + +:mps:tag:`section.comment` Each section should start with a banner comment (see +.comment.banner) describing what the section contains. + +:mps:tag:`para` Within sections, code often breaks down into natural units called +"paragraphs". A paragraph might be a set of strongly related +declarations (Init and Finish, for example), or a few lines of code +which it makes sense to consider together (the assignment of fields into +a structure, for example). + +:mps:tag:`para.space` Precede paragraphs by a single blank line. + +Statements +.......... + +:mps:tag:`statement.one` Generally only have at most one statement per line. In +particular the following are deprecated:: + + if (thing) return; + + a=0; b=0; + + case 0: f = inRampMode ? AMCGen0RampmodeFrequency : AMCGen0Frequency; + +:mps:tag:`statement.one.why` Debuggers can often only place breakpoints on lines, +not expressions or statements within a line. The ``if (thing) return;`` is +a particularly important case, if thing is a reasonably rare return +condition then you might want to breakpoint it in a debugger session. +Annoying because ``if (thing) return;`` is quite compact and pleasing +otherwise. + +Indentation +........... + +:mps:tag:`indent` Indent the body of a block by two spaces. For formatting +purposes, the "body of a block" means: + +- statements between braces, +- a single statement following a lone ``if``; +- statements in a switch body; see .switch. + +(:mps:tag:`indent.logical` The aim is to group what we think of as logical +blocks, even though they may not exactly match how "block" is used in +the definition of C syntax). + +Some examples:: + + if (res != ResOK) { + SegFinish(&span->segStruct); + PoolFreeP(MV->spanPool, span, sizeof(SpanStruct)); + return res; + } + + if (res != ResOK) + goto error; + + if (j == block->base) { + if (j+step == block->limit) { + if (block->thing) + putc('@', stream); + } + } else if (j+step == block->limit) { + putc(']', stream); + pop_bracket(); + } else { + putc('.', stream); + } + + switch (c) { + case 'A': + c = 'A'; + p += 1; + break; + } + +:mps:tag:`indent.goto-label` Place each goto-label on a line of its own, +outdented to the same level as the surrounding block. Then indent the +non-label part of the statement normally. + +:: + + result foo(void) + { + statement(); + if (error) + goto foo; + statement(); + return OK; + + foo: + unwind(); + return ERROR; + } + +:mps:tag:`indent.case-label` Outdent case- and default-labels in a switch +statement in the same way as :mps:ref:`.indent.goto-label`. See :mps:ref:`.switch`. + +:mps:tag:`indent.cont` If an expression or statement won't fit on a single line, +indent the continuation lines by two spaces, apart from the following +exception: + +:mps:tag:`indent.cont.parens` if you break a statement inside a parameter list or +other parenthesized expression, indent so that the continuation lines up +just after the open parenthesis. For example:: + + PoolClassInit(&PoolClassMVStruct, + "MV", init, finish, allocP, freeP, + NULL, NULL, describe, isValid); + +:mps:tag:`indent.cont.expr` Note that when breaking an expression it is clearer +to place the operator at the start of the continuation line:: + + CHECKL(AddrAdd((Addr)chunk->pageTableMapped, + BTSize(chunk->pageTablePages)) + <= AddrAdd(chunk->base, chunk->ullageSize)); + +This is particularly useful in long conditional expressions that use && +and ||. For example:: + + } while(trace->state != TraceFINISHED + && (trace->emergency || traceWorkClock(trace) < pollEnd)); + +:mps:tag:`indent.hint` Usually, it is possible to determine the correct +indentation for a line by looking to see if the previous line ends with +a semicolon. If it does, indent to the same amount, otherwise indent by +two more spaces. The main exceptions are lines starting with a close +brace, goto-labels, and line-breaks between parentheses. + +Positioning of Braces +..................... + +:mps:tag:`brace.otb` Use the "One True Brace" (or OTB) style. This places the +open brace after the control word or expression, separated by a space, +and when there is an else, places that after the close brace. For +example:: + + if(isBase) { + new->base = limit; + new->limit = block->limit; + block->limit = base; + new->next = block->next; + block->next = new; + } else { + new->base = block->base; + new->limit = base; + block->base = limit; + new->next = block; + *prev = new; + } + +The same applies to struct, enum, union. + +:mps:tag:`brace.otb.function.not` OTB is never used for function definitions. + +:mps:tag:`brace.always` Braces are always required after ``if``, ``else``, ``switch``, +``while``, ``do``, and ``for``. + +:mps:tag:`brace.always.except` Except that a lone ``if`` with no ``else`` is allowed +to drop its braces when its body is a single simple statement. Typically +this will be a ``goto`` or an assignment. For example:: + + if (res != ResOK) + goto failStart; + +Note in particular that an ``if`` with an ``else`` must have braces on both +paths. + +Switch Statements +................. + +:mps:tag:`switch` format switch statements like this:: + + switch (action) { + case WIBBLE: + case WOBBLE: + { + int angle; + err = move(plate, action, &angle); + } + break; + + case QUIET: + drop(); + /* fall-through */ + + case QUIESCENT: + err = 0; + break; + + default: + NOTREACHED; + break; + } + +The component rules that result in this style are: + +:mps:tag:`switch.break` The last line of every case-clause body must be an +unconditional jump statement (usually ``break``, but may be ``goto``, +``continue``, or ``return``), or if a fall-through is intended, the comment +``/* fall-through */``. (Note: if the unconditional jump should never be +taken, because of previous conditional jumps, use :c:macro:`NOTREACHED` on the +line before it). This rule is to prevent accidental fall-throughs, even +if someone makes a editing mistake that causes a conditional jump to be +missed. + +:mps:tag:`switch.default` It is usually a good idea to have a default-clause, +even if all it contains is :c:macro:`NOTREACHED` and ``break``. Remember that +:c:macro:`NOTREACHED` doesn't stop the process in all build varieties. + + +Formatting Comments +................... + +:mps:tag:`comment` There are three types of comments: banners, paragraph +comments, and columnar comments. + +:mps:tag:`comment.banner` Banner comments come at the start of sections. A banner +comment consists of a heading usually composed of a symbol, an em-dash +(--) and an short explanation, followed by English text which is +formatted using conventional text documentation guidelines (see +guide.text). The open and close comment tokens (``/*`` and ``*/``) are +placed at the top and bottom of a column of asterisks. The text is +separated from the asterisks by one space. Place a blank line between +the banner comment and the section it comments. For example:: + + /* BlockStruct -- Block descriptor + * + * The pool maintains a descriptor structure for each + * contiguous allocated block of memory it manages. + * The descriptor is on a simple linked-list of such + * descriptors, which is in ascending order of address. + */ + + typedef struct BlockStruct { + +:mps:tag:`comment.para` Paragraph comments come at the start of paragraphs in the +code. A paragraph comment consists of formatted English text, with each +line wrapped by the open and close comment tokens (``/*`` and ``*/``). +(This avoids problems when cutting and pasting comments.) For example:: + + /* If the freed area is in the base sentinel then insert */ + /* the new descriptor after it, otherwise insert before. */ + if(isBase) { + +:mps:tag:`comment.para.precede` Paragraph comments, even one-liners, precede the +code to which they apply. + +:mps:tag:`comment.column` Columnar comments appear in a column to the right of +the code. They should be used sparingly, since they clutter the code and +make it hard to edit. Use them on variable declarations and structure, +union, or enum declarations. They should start at least at column 32 +(counting from 0, that is, on a tab-stop), and should be terse +descriptive text. Abandon English sentence structure if this makes the +comment clearer. Don't write more than one line. Here's an example:: + + typedef struct PoolMVStruct { + Pool blockPool; /* for block descriptors */ + Pool spanPool; /* for span descriptors */ + size_t extendBy; /* size to extend pool by */ + size_t avgSize; /* estimate of allocation size */ + size_t maxSize; /* estimate of maximum size */ + Addr space; /* total free space in pool */ + Addr lost; /* lost when free can't allocate */ + struct SpanStruct *spans; /* span chain */ + } PoolMVStruct; + +Macros +...... + +:mps:tag:`macro.careful` Macros in C are a real horror bag, be extra careful. +There's lots that could go here, but proper coverage probably deserves a +separate document. Which isn't written yet. + +:mps:tag:`macro.general` Do try and follow the other formatting conventions for +code in macro definitions. + +:mps:tag:`macro.backslash` Backslashes used for continuation lines in macro +definitions should be put on the right somewhere where they will be less +in the way. Example:: + + #define RAMP_RELATION(X) \ + X(RampOUTSIDE, "outside ramp") \ + X(RampBEGIN, "begin ramp") \ + X(RampRAMPING, "ramping") \ + X(RampFINISH, "finish ramp") \ + X(RampCOLLECTING, "collecting ramp") + + +History +------- + +- 2007-06-04 DRJ_ Adopted from Harlequin MMinfo version and edited. + +- 2007-06-04 DRJ_ Changed .width from 80 to 72. Banned space between + ``if`` and ``(``. Required braces on almost everything. Clarified that + paragraph comments precede the code. + +- 2007-06-13 RHSK_ Removed .brace.block, because MPS source always + uses .brace.otb. Remove .indent.elseif because it is obvious (ahem) and + showing an example is sufficient. New rules for .switch.*: current MPS + practice is a mess, so lay down a neat new law. + +- 2007-06-27 RHSK_ Added :mps:ref:`.space.function.not`. + +- 2007-07-17 DRJ_ Added .macro.\* + +- 2012-09-26 RB_ Converted to Markdown and reversed inconsistent + switch "law". + +.. _DRJ: http://www.ravenbrook.com/consultants/drj +.. _RHSK: http://www.ravenbrook.com/consultants/rhsk +.. _RB: http://www.ravenbrook.com/consultants/rb + + +Copyright and License +--------------------- + +This document is copyright © 2002-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 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/mps/manual/html/_sources/design/index.txt b/mps/manual/html/_sources/design/index.txt index ce234220b78..a0c98237228 100644 --- a/mps/manual/html/_sources/design/index.txt +++ b/mps/manual/html/_sources/design/index.txt @@ -9,6 +9,7 @@ Design config critical-path guide.hex.trans + guide.impl.c.format keyword-arguments ring sig diff --git a/mps/manual/html/_sources/design/interface-c.txt b/mps/manual/html/_sources/design/interface-c.txt new file mode 100644 index 00000000000..b8db5831226 --- /dev/null +++ b/mps/manual/html/_sources/design/interface-c.txt @@ -0,0 +1,331 @@ +.. _design-interface-c: + + +.. index:: + pair: C interface; design + + +C interface design +================== + +.. mps:prefix:: design.mps.interface.c + + +Introduction +------------ + +:mps:tag:`scope` This document is the design for the Memory Pool System +(MPS) interface to the C Language, impl.h.mps. + +:mps:tag:`bg` See mail.richard.1996-07-24.10-57. + + +Analysis +-------- + +Goals +..... + +:mps:tag:`goal.c` The file impl.h.mps is the C external interface to the +MPS. It is the default interface between client code written in C and +the MPS. :mps:tag:`goal.cpp` impl.h.mps is not specifically designed to be +an interface to C++, but should be usable from C++. + + +Requirements +............ + +:mps:tag:`req` The interface must provide an interface from client code +written in C to the functionality of the MPS required by the product +(see req.product), Dylan (req.dylan), and the Core RIP (req.epcore). + +``mps.h`` may not include internal MPS header files (such as +``pool.h``). + +It is essential that the interface cope well with change, in order to +avoid restricting possible future MPS developments. This means that +the interface must be "open ended" in its definitions. This accounts +for some of the apparently tortuous methods of doing things +(``mps_fmt_A_t``, for example). The requirement is that the MPS should +be able to add new functionality, or alter the implementation of +existing functionality, without affecting existing client code. A +stronger requirement is that the MPS should be able to change without +*recompiling* client code. This is not always possible. + +.. note:: + + `.naming.global` was presumably done in response to unwritten + requirements regarding the use of the name spaces in C, perhaps + these: + + - :mps:tag:`req.name.iso` The interface shall not conflict in terms of + naming with any interfaces specified by ISO C and all reasonable + future versions. + + - :mps:tag:`req.name.general` The interface shall use a documented and + reasonably small portion of the namespace so that clients can + interoperate easily. + + David Jones, 1998-10-01. + + +Architecture +------------ + +:mps:tag:`fig.arch` The architecture of the MPS Interface + +[missing figure] + +Just behind ``mps.h`` is the file ``mpsi.c``, the "MPS interface +layer" which does the job of converting types and checking parameters +before calling through to the MPS proper, using internal MPS methods. + + +General conventions +------------------- + +:mps:tag:`naming` The external interface names should adhere to the +documented interface conventions; these are found in +doc.mps.ref-man.if-conv(0).naming. They are paraphrased/recreated +here. + +:mps:tag:`naming.unixy` The external interface does not follow the same +naming conventions as the internal code. The interface is designed to +resemble a more conventional C, Unix, or Posix naming convention. + +:mps:tag:`naming.case` Identifiers are in lower case, except +non-function-like macros, which are in upper case. + +:mps:tag:`naming.global` All publicised identifiers are +prefixed ``mps_`` or :c:macro:`MPS_`. + +:mps:tag:`naming.all` All identifiers defined by the MPS +should begin ``mps_`` or :c:macro:`MPS_` or ``_mps_``. + +:mps:tag:`naming.type` Types are suffixed ``_t``. + +:mps:tag:`naming.struct` Structure types and tags are suffixed ``_s``. + +:mps:tag:`naming.union` Unions types and tags are suffixed ``_u``. + +:mps:tag:`naming.scope` The naming conventions apply to all identifiers (see +ISO C §6.1.2); this includes names of functions, variables, types +(through typedef), structure and union tags, enumeration members, +structure and union members, macros, macro parameters, labels. + +:mps:tag:`naming.scope.labels` labels (for ``goto`` statements) should be +rare, only in special block macros and probably not even then. + +.. note:: + + This principle is not adhered to in the source code, which uses + ``goto`` for handling error cases. Gareth Rees, 2013-05-27. + +:mps:tag:`naming.scope.other` The naming convention would also extend to +enumeration types and parameters in functions prototypes but both of +those are prohibited from having names in an interface file. + +:mps:tag:`type.gen` The interface defines memory addresses as ``void *`` and +sizes as ``size_t`` for compatibility with standard C (in particular, +with :c:func:`malloc()`). These types must be binary compatible with the +internal types :c:type:`Addr` and :c:type:`Size` respectively. Note that this +restricts the definitions of the internal types :c:type:`Addr` and :c:type:`Size` +when the MPS is interfaced with C, but does not restrict the MPS in +general. + +:mps:tag:`type.opaque` Opaque types are defined as pointers to structures +which are never defined. These types are cast to the corresponding +internal types in ``mpsi.c``. + +:mps:tag:`type.trans` Some transparent structures are defined. The client is +expected to read these, or poke about in them, under restrictions +which should be documented. The most important is probably the +allocation point (:c:type:`mps_ap_s`) which is part of allocation buffers. +The transparent structures must be binary compatible with +corresponding internal structures. For example, the fields of +:c:type:`mps_ap_s` must correspond with :c:type:`APStruct` internally. This is +checked by ``mpsi.c`` in :c:func:`mps_check()`. + +:mps:tag:`type.pseudo` Some pseudo-opaque structures are defined. These only +exist so that code can be inlined using macros. The client code +shouldn't mess with them. The most important case of this is the scan +state (:c:type:`mps_ss_s`) which is accessed by the in-line scanning macros, +``MPS_SCAN_*`` and ``MPS_FIX*``. + +:mps:tag:`type.enum` There should be no enumeration types in the interface. +Note that enum specifiers (to declare integer constants) are fine as +long as no type is declared. See guide.impl.c.misc.enum.type. + +:mps:tag:`type.fun` Whenever function types or derived function types (such +as pointer to function) are declared a prototype should be used and +the parameters to the function should not be named. This includes the +case where you are declaring the prototype for an interface function. + +:mps:tag:`type.fun.example` So use:: + + extern mps_res_t mps_alloc(mps_addr_t *, mps_pool_t, size_t, ...); + +rather than:: + + extern mps_res_t mps_alloc(mps_addr_t *addr_return, mps_pool_t pool , size_t size, ...); + +and:: + + typedef mps_addr_t (*mps_fmt_class_t)(mps_addr_t); + +rather than:: + + typedef mps_addr_t (*mps_fmt_class_t)(mps_addr_t object); + +See guide.impl.c.misc.prototype.parameters. + + +Checking +-------- + +:mps:tag:`check.space` When the arena needs to be recovered from a parameter +it is check using ``AVERT(Foo, foo)`` before any attempt to call +``FooArena(foo)``. The macro :c:func:`AVERT()` in impl.h.assert performs +simple thread-safe checking of ``foo``, so it can be called outside of +:c:func:`ArenaEnter()` and :c:func:`ArenaLeave()`. + +:mps:tag:`check.types` We use definitions of types in both our external +interface and our internal code, and we want to make sure that they +are compatible. (The external interface changes less often and hides +more information.) At first, we were just checking their sizes, which +wasn't very good, but I've come up with some macros which check the +assignment compatibility of the types too. This is a sufficiently +useful trick that I thought I'd send it round. It may be useful in +other places where types and structures need to be checked for +compatibility at compile time. + +These macros don't generate warnings on the compilers I've tried. + +.. c:function:: COMPATLVALUE(lvalue1, lvalue2) + +This macro checks the assignment compatibility of two lvalues. It uses +:c:func:`sizeof()` to ensure that the assignments have no effect. :: + + #define COMPATLVALUE(lv1, lv2) \ + ((void)sizeof((lv1) = (lv2)), (void)sizeof((lv2) = (lv1)), TRUE) + +.. c:function:: COMPATTYPE(type1, type2) + +This macro checks that two types are assignment-compatible and equal +in size. The hack here is that it generates an lvalue for each type by +casting zero to a pointer to the type. The use of :c:func:`sizeof()` avoids +the undefined behaviour that would otherwise result from dereferencing +a null pointer. :: + + #define COMPATTYPE(t1, t2) \ + (sizeof(t1) == sizeof(t2) && \ + COMPATLVALUE(*((t1 *)0), *((t2 *)0))) + +.. c:function:: COMPATFIELDAPPROX(structure1, field1, structure2, field2) + +This macro checks that the offset and size of two fields in two +structure types are the same. :: + + #define COMPATFIELDAPPROX(s1, f1, s2, f2) \ + (sizeof(((s1 *)0)->f1) == sizeof(((s2 *)0)->f2) && \ + offsetof(s1, f1) == offsetof(s2, f2)) + +.. c:function:: COMPATFIELD(structure1, field1, structure2, field2) + +This macro checks the offset, size, and assignment-compatibility of +two fields in two structure types. :: + + #define COMPATFIELD(s1, f1, s2, f2) \ + (COMPATFIELDAPPROX(s1, f1, s2, f2) && \ + COMPATLVALUE(((s1 *)0)->f1, ((s2 *)0)->f2)) + + +Binary compatibility issues +--------------------------- + +As in, "Enumeration types are not allowed" (see +mail.richard.1995-09-08.09-28). + +There are two main aspects to run-time compatibility: binary interface +and protocol. The binary interface is all the information needed to +correctly use the library, and includes external symbol linkage, +calling conventions, type representation compatibility, structure +layouts, etc. The protocol is how the library is actually used by the +client code -- whether this is called before that -- and determines +the semantic correctness of the client with respect to the library. + +The binary interface is determined completely by the header file and +the target. The header file specifies the external names and the +types, and the target platform specifies calling conventions and type +representation. There is therefore a many-to-one mapping between the +header file version and the binary interface. + +The protocol is determined by the implementation of the library. + + +Constraints +----------- + +:mps:tag:`cons` The MPS C Interface constrains the MPS in order to provide +useful memory management services to a C or C++ program. + +:mps:tag:`cons.addr` The interface constrains the MPS address type, Addr +(design.mps.type.addr), to being the same as C's generic pointer type, +``void *``, so that the MPS can manage C objects in the natural way. + +:mps:tag:`pun.addr` We pun the type of :c:type:`mps_addr_t` (which is ``void *``) +into :c:type:`Addr` (an incomplete type, see design.mps.type.addr). This +happens in the call to the scan state's fix function, for example. + +:mps:tag:`cons.size` The interface constrains the MPS size type, :c:type:`Size` +(design.mps.type.size), to being the same as C's size type, +``size_t``, so that the MPS can manage C objects in the natural way. + +:mps:tag:`pun.size` We pun the type of ``size_t`` in mps.h into :c:type:`Size` in +the MPM, as an argument to the format methods. We assume this works. + +:mps:tag:`cons.word` The MPS assumes that :c:type:`Word` (design.mps.type.word) +and :c:type:`Addr` (design.mps.type.addr) are the same size, and the +interface constrains :c:type:`Word` to being the same size as C's generic +pointer type, ``void *``. + + +Notes +----- + +The file ``mpstd.h`` is the MPS target detection header. It decodes +preprocessor symbols which are predefined by build environments in +order to determine the target platform, and then defines uniform +symbols, such as :c:macro:`MPS_ARCH_I3`, for use internally by the MPS. + +There is a design document for the mps interface, +design.mps.interface, but it was written before we had the idea of +having a C interface layer. It is quite relevant, though, and could be +updated. We should use it during the review. + +All exported identifiers and file names should begin with ``mps_`` or +:c:macro:`MPS_` so that they don't clash with other systems. + +We should probably have a specialized set of rules and a special +checklist for this interface. + +:mps:tag:`fmt.extend` This paragraph should be an explanation of why +``mps_fmt_A_t`` is so called. The underlying reason is future +extensibility. + +:mps:tag:`thread-safety` Most calls through this interface lock the space +and therefore make the MPM single-threaded. In order to do this they +must recover the space from their parameters. Methods such as +:c:func:`ThreadSpace()` must therefore be callable when the space is *not* +locked. These methods are tagged with the tag of this note. + +:mps:tag:`lock-free` Certain functions inside the MPM are thread-safe and do +not need to be serialized by using locks. They are marked with the tag +of this note. + +:mps:tag:`form` Almost all functions in this implementation simply cast +their arguments to the equivalent internal types, and cast results +back to the external type, where necessary. Only exceptions are noted +in comments. + + diff --git a/mps/manual/html/_sources/design/io.txt b/mps/manual/html/_sources/design/io.txt new file mode 100644 index 00000000000..d5cecd5b936 --- /dev/null +++ b/mps/manual/html/_sources/design/io.txt @@ -0,0 +1,407 @@ +.. _design-io: + + +.. index:: + pair: I/O subsystem; design + + +I/O subsystem +============= + +.. mps:prefix:: design.mps.io + + +Introduction +------------ + +:mps:tag:`intro` This document is the design of the MPS I/O Subsystem, a +part of the plinth. + +:mps:tag:`readership` This document is intended for MPS developers. + + +Background +---------- + +:mps:tag:`bg` This design is partly based on the design of the Internet User +Datagram Protocol (UDP). Mainly I used this to make sure I hadn't left +out anything which we might need. + + +Purpose +------- + +:mps:tag:`purpose` The purpose of the MPS I/O Subsystem is to provide a +means to measure, debug, control, and test a memory manager build +using the MPS. + +:mps:tag:`purpose.measure` Measurement consists of emitting data which can +be collected and analysed in order to improve the attributes of +application program, quite possibly by adjusting parameters of the +memory manager (see overview.mps.usage). + +:mps:tag:`purpose.control` Control means adjusting the behaviour of the MM +dynamically. For example, one might want to adjust a parameter in +order to observe the effect, then transfer that adjustment to the +client application later. + +:mps:tag:`purpose.test` Test output can be used to ensure that the memory +manager is behaving as expected in response to certain inputs. + + +Requirements +------------ + +General +....... + +:mps:tag:`req.fun.non-hosted` The MPM must be a host-independent system. + +:mps:tag:`req.attr.host` It should be easy for the client to set up the MPM +for a particular host (such as a washing machine). + +Functional +.......... + +:mps:tag:`req.fun.measure` The subsystem must allow the MPS to transmit +quantitative measurement data to an external tool so that the system +can be tuned. + +:mps:tag:`req.fun.debug` The subsystem must allow the MPS to transmit +qualitative information about its operation to an external tool so +that the system can be debugged. + +:mps:tag:`req.fun.control` The subsystem must allow the MPS to receive +control information from an external tool so that the system can be +adjusted while it is running. + +:mps:tag:`req.dc.env.no-net` The subsystem should operate in environments +where there is no networking available. + +:mps:tag:`req.dc.env.no-fs` The subsystem should operate in environments +where there is no filesystem available. + + +Architecture +------------ + +:mps:tag:`arch.diagram` I/O Architecture Diagram + +[missing diagram] + +:mps:tag:`arch.int` The I/O Interface is a C function call interface by +which the MPM sends and receives "messages" to and from the hosted I/O +module. + +:mps:tag:`arch.module` The modules are part of the MPS but not part of the +freestanding core system (see design.mps.exec-env). The I/O module is +responsible for transmitting those messages to the external tools, and +for receiving messages from external tools and passing them to the +MPM. + +:mps:tag:`arch.module.example` For example, the "file implementation" might +just send/write telemetry messages into a file so that they can be +received/read later by an off-line measurement tool. + +:mps:tag:`arch.external` The I/O Interface is part of interface to the +freestanding core system (see design.mps.exec-env). This is so that +the MPS can be deployed in a freestanding environment, with a special +I/O module. For example, if the MPS is used in a washing machine the +I/O module could communicate by writing output to the seven-segment +display. + + +Example configurations +...................... + +:mps:tag:`example.telnet` This shows the I/O subsystem communicating with a +telnet client over a TCP/IP connection. In this case, the I/O +subsystem is translating the I/O Interface into an interactive text +protocol so that the user of the telnet client can talk to the MM. + +[missing diagram] + +:mps:tag:`example.file` This shows the I/O subsystem dumping measurement +data into a file which is later read and analysed. In this case the +I/O subsystem is simply writing out binary in a format which can be +decoded. + +[missing diagram] + +:mps:tag:`example.serial` This shows the I/O subsystem communicating with a +graphical analysis tool over a serial link. This could be useful for a +developer who has two machines in close proximity and no networking +support. + +:mps:tag:`example.local` In this example the application is talking directly +to the I/O subsystem. This is useful when the application is a +reflective development environment (such as MLWorks) which wants to +observe its own behaviour. + +[missing diagram] + + +Interface +--------- + +:mps:tag:`if.msg` The I/O interface is oriented around opaque binary +"messages" which the I/O module must pass between the MPM and external +tools. The I/O module need not understand or interpret the contents of +those messages. + +:mps:tag:`if.msg.opaque` The messages are opaque in order to minimize the +dependency of the I/O module on the message internals. It should be +possible for clients to implement their own I/O modules for unusual +environments. We do not want to reveal the internal structure of our +data to the clients. Nor do we want to burden them with the details of +our protocols. We'd also like their code to be independent of ours, so +that we can expand or change the protocols without requiring them to +modify their modules. + +:mps:tag:`if.msg.dgram` Neither the MPM nor the external tools should assume +that the messages will be delivered in finite time, exactly once, or +in order. This will allow the I/O modules to be implemented using +unreliable transport layers such as the Internet User Datagram Protocl +(UDP). It will also give the I/O module the freedom to drop +information rather than block on a congested network, or stop the +memory manager when the disk is full, or similar events which really +shouldn't cause the memory manager to stop working. The protocols we +need to implement at the high level can be design to be robust against +lossage without much difficulty. + + +I/O module state +................ + +:mps:tag:`if.state` The I/O module may have some internal state to preserve. +The I/O Interface defines a type for this state, :c:type:`mps_io_t`, a +pointer to an incomplete structure :c:type:`mps_io_s`. The I/O module is at +liberty to define this structure. + + +Message types +............. + +:mps:tag:`if.type` The I/O module must be able to deliver messages of +several different types. It will probably choose to send them to +different destinations based on their type: telemetry to the +measurement tool, debugging output to the debugger, etc. :: + + typedef int mps_io_type_t; + enum { + MPS_IO_TYPE_TELEMETRY, + MPS_IO_TYPE_DEBUG + }; + + +Limits +...... + +:mps:tag:`if.message-max` The interface will define an unsigned integral +constant :c:macro:`MPS_IO_MESSAGE_MAX` which will be the maximum size of +messages that the MPM will pass to :c:func:`mps_io_send()` (:mps:ref:`.if.send`) and +the maximum size it will expect to receive from :c:func:`mps_io_receive()`. + + +Interface set-up and tear-down +.............................. + +:mps:tag:`if.create` The MPM will call :c:func:`mps_io_create()` to set up the I/O +module. On success, this function should return :c:macro:`MPS_RES_OK`. It may +also initialize a "state" value which will be passed to subsequent +calls through the interface. + +:mps:tag:`if.destroy` The MPM will call :c:func:`mps_io_destroy()` to tear down +the I/O module, after which it guarantees that the state value will +not be used again. The ``state`` parameter is the state previously +returned by :c:func:`mps_io_create()` (:mps:ref:`.if.create`). + +Message send and receive +........................ + +.. c:function:: extern mps_res_t mps_io_send(mps_io_t state, mps_io_type_t type, void *message, size_t size) + +:mps:tag:`if.send` The MPM will call :c:func:`mps_io_send()` when it wishes to +send a message to a destination. The ``state`` parameter is the state +previously returned by :c:func:`mps_io_create()` (:mps:ref:`.if.create`). The +``type`` parameter is the type (:mps:ref:`.if.type`) of the message. The +``message`` parameter is a pointer to a buffer containing the message, +and ``size`` is the length of that message, in bytes. The I/O module +must make an effort to deliver the message to the destination, but is +not expected to guarantee delivery. The function should return +:c:macro:`MPS_RES_IO` only if a serious error occurs that should cause the +MPM to return with an error to the client application. Failure to +deliver the message does not count. + +.. note:: + + Should there be a timeout parameter? What are the timing + constraints? :c:func:`mps_io_send()` shouldn't block. + +.. c:function:: extern mps_res_t mps_io_receive(mps_io_t state, void **buffer_o, size_t *size_o) + +:mps:tag:`if.receive` The MPM will call :c:func:`mps_io_receive()` when it wants +to see if a message has been sent to it. The ``state`` parameter is +the state previously returned by :c:func:`mps_io_create()` (:mps:ref:`.if.create`). +The ``buffer_o`` parameter is a pointer to a value which should be +updated with a pointer to a buffer containing the message received. +The ``size_o`` parameter is a pointer to a value which should be +updated with the length of the message received. If there is no +message ready for receipt, the length returned should be zero. + +.. note:: + + Should we be able to receive truncated messages? How can this be + done neatly? + + +I/O module implementations +-------------------------- + +Routeing +........ + +The I/O module must decide where to send the various messages. A +file-based implementation could put them in different files based on +their types. A network-based implementation must decide how to address +the messages. In either case, any configuration must either be +statically compiled into the module, or else read from some external +source such as a configuration file. + + +Notes +----- + +The external tools should be able to reconstruct stuff from partial +info. For example, you come across a fragment of an old log containing +just a few old messages. What can you do with it? + +Here's some completely untested code which might do the job for UDP. +:: + + #include "mpsio.h" + + #include + #include + #include + #include + #include + #include + + typedef struct mps_io_s { + int sock; + struct sockaddr_in mine; + struct sockaddr_in telemetry; + struct sockaddr_in debugging; + } mps_io_s; + + static mps_bool_t inited = 0; + static mps_io_s state; + + + mps_res_t mps_io_create(mps_io_t *mps_io_o) + { + int sock, r; + + if(inited) + return MPS_RES_LIMIT; + + state.mine = /* setup somehow from config */; + state.telemetry = /* setup something from config */; + state.debugging = /* setup something from config */; + + /* Make a socket through which to communicate. */ + sock = socket(AF_INET, SOCK_DGRAM, 0); + if(sock == -1) return MPS_RES_IO; + + /* Set socket to non-blocking mode. */ + r = fcntl(sock, F_SETFL, O_NDELAY); + if(r == -1) return MPS_RES_IO; + + /* Bind the socket to some UDP port so that we can receive messages. */ + r = bind(sock, (struct sockaddr *)&state.mine, sizeof(state.mine)); + if(r == -1) return MPS_RES_IO; + + state.sock = sock; + + inited = 1; + + *mps_io_o = &state; + return MPS_RES_OK; + } + + + void mps_io_destroy(mps_io_t mps_io) + { + assert(mps_io == &state); + assert(inited); + + (void)close(state.sock); + + inited = 0; + } + + + mps_res_t mps_io_send(mps_io_t mps_io, mps_type_t type, + void *message, size_t size) + { + struct sockaddr *toaddr; + + assert(mps_io == &state); + assert(inited); + + switch(type) { + MPS_IO_TYPE_TELEMETRY: + toaddr = (struct sockaddr *)&state.telemetry; + break; + + MPS_IO_TYPE_DEBUGGING: + toaddr = (struct sockaddr *)&state.debugging; + break; + + default: + assert(0); + return MPS_RES_UNIMPL; + } + + (void)sendto(state.sock, message, size, 0, toaddr, sizeof(*toaddr)); + + return MPS_RES_OK; + } + + + mps_res_t mps_io_receive(mps_io_t mps_io, + void **message_o, size_t **size_o) + { + int r; + static char buffer[MPS_IO_MESSAGE_MAX]; + + assert(mps_io == &state); + assert(inited); + + r = recvfrom(state.sock, buffer, sizeof(buffer), 0, NULL, NULL); + if(r == -1) + switch(errno) { + /* Ignore interrupted system calls, and failures due to lack */ + /* of resources (they might go away.) */ + case EINTR: case ENOMEM: case ENOSR: + r = 0; + break; + + default: + return MPS_RES_IO; + } + + *message_o = buffer; + *size_o = r; + return MPS_RES_OK; + } + + +Attachments +----------- + +"O Architecture Diagram" +"O Configuration Diagrams" + + diff --git a/mps/manual/html/_sources/design/keyword-arguments.txt b/mps/manual/html/_sources/design/keyword-arguments.txt index eb21b41d92a..179ba64fde9 100644 --- a/mps/manual/html/_sources/design/keyword-arguments.txt +++ b/mps/manual/html/_sources/design/keyword-arguments.txt @@ -1,6 +1,148 @@ +.. _design-keyword-arguments: + + .. index:: pair: keyword arguments; design -.. _design-keyword-arguments: -.. include:: ../../converted/keyword-arguments.rst +Keyword arguments in the MPS +============================ + + + +Introduction +------------ +Up to version 1.111, the `Memory Pool System +`_ used varags to pass arguments +to arena and pool classes, because the general MPS interface can't +specify what arguments those classes might need in C prototypes. This +mechanism was error-prone and did not allow for any optional arguments, +meaning that the client had to specify or predict esoteric tuning +parameters. + +Starting with version 1.112, the MPS uses an idiom for keyword arguments. + + +Overview +-------- +The basic design is not specific to the MPS. The keyword argument list is +passed as an array of argument structures which look like this:: + + typedef struct mps_key_s *mps_key_t; + typedef struct mps_arg_s { + mps_key_t key; + union { + int i; + char c; + void *p; + size_t size; + /* etc. */ + } val; + } mps_arg_s; + +The argument list is assembled and passed like this:: + + mps_arg_s args[3]; + args[0].key = MPS_KEY_MIN_SIZE; + args[0].val.size = 32; + args[1].key = MPS_KEY_MAX_SIZE; + args[1].val.size = 1024; + args[2].key = MPS_KEY_ARGS_END; + mps_pool_create_k(&pool, some_pool_class(), args); + +This can be written quite concisely in C99:: + + mps_pool_create_k(&pool, some_pool_class(), + (mps_arg_s []){{MPS_KEY_MIN_SIZE, {.size = 32}}, + {MPS_KEY_MAX_SIZE, {.size = 1024}}, + {MPS_KEY_ARGS_END}}); + +The arguments that are recognised and used by the function are removed +from the array (and the subsequent arguments moved up) so that if they +are all consumed the array has :c:macro:`MPS_KEY_ARGS_END` in slot zero on +return. This can be checked by the caller. + +- It's not a static error to pass excess arguments. This makes it easy to + substitute one pool or arena class for another (which might ignore some + arguments). The caller can check that ``args[0].key`` is + :c:macro:`MPS_KEY_ARGS_END` if desired. + +- NULL is not a valid argument list. This is in line with general MPS + design principles to avoid accidental omissions. For convenience, we + provide ``mps_args_none`` as a static empty argument list. + +- NULL is not a valid argument key. This is in line with general MPS + design principles to avoid accidental omissions. Every key points to + a structure with a signature that can be checked. This makes it virtually + impossible to get an argument list with bad keys or that is unterminated + past MPS checking. + + +Internals +--------- +Internally, keys are static constant structures which are signed and contain +a checking method for the argument, like this:: + + typedef struct mps_arg_s *Arg; + typedef struct mps_key_s { + Sig sig; /* Always KeySig */ + const char *name; + Bool check(Arg arg); + } KeyStruct; + +They are mostly declared in the modules that consume them, except for a few +common keys. Declarations look like:: + + const KeyStruct _mps_key_extend_by = {KeySig, "extend_by", ArgCheckSize}; + +but ``arg.h`` provides a macro for this:: + + ARG_DEFINE_KEY(extend_by, Size); + +We define keys as static structures (rather than, say, an enum) because: + +- The set of keys can be extended indefinitely. +- The set of keys can be extended by indepdently linked modules. +- The structure contents allow strong checking of argument lists. + +In the MPS Interface, we declare keys like this:: + + extern const struct mps_key_s _mps_key_extend_by; + #define MPS_KEY_EXTEND_BY (&_mps_key_extend_by) + +The underscore on the symbol requests that client code doesn't reference +it, but instead uses the macro. This gives us adaptability to change the +design and replace keys with, say, magic numbers. + + +The varargs legacy +------------------ +For backward compatibility, varargs to arena and pool creation are +converted into keyword arguments by position, using a method in the +arena or pool class. For example:: + + static void MVVarargs(ArgStruct args[], va_list varargs) + { + args[0].key = MPS_KEY_EXTEND_BY; + args[0].val.size = va_arg(varargs, Size); + args[1].key = MPS_KEY_MEAN_SIZE; + args[1].val.size = va_arg(varargs, Size); + args[2].key = MPS_KEY_MAX_SIZE; + args[2].val.size = va_arg(varargs, Size); + args[3].key = MPS_KEY_ARGS_END; + AVER(ArgListCheck(args)); + } + +This leaves the main body of code, and any future code, free to just +handle keyword arguments only. + +The use of varargs is deprecated in the manual and the interface and these +methods can be deleted at some point in the future. + + +References +---------- + +.. [RB_2012-05-24] Richard Brooksby. Ravenbrook Limited. 2012-05-24. "`Keyword and optional arguments `__". + + diff --git a/mps/manual/html/_sources/design/lib.txt b/mps/manual/html/_sources/design/lib.txt new file mode 100644 index 00000000000..e176f8f6995 --- /dev/null +++ b/mps/manual/html/_sources/design/lib.txt @@ -0,0 +1,102 @@ +.. _design-lib: + + +.. index:: + pair: library interface; design + + +Library interface +================= + +.. mps:prefix:: design.mps.lib + + +Introduction +------------ + +:mps:tag:`intro` This document is the design of the MPS Library Interface, a +part of the plinth. + +:mps:tag:`readership` Any MPS developer. Any clients that are prepared to +read this in order to get documentation. + + +Goals +----- + +:mps:tag:`goal` The goals of the MPS library interface are: + +:mps:tag:`goal.host` To control the dependency of the MPS on the hosted ISO +C library so that the core MPS remains freestanding (see +design.mps.exec-env). + +:mps:tag:`goal.free` To allow the core MPS convenient access to ISO C +functionality that is provided on freestanding platforms (see +design.mps.exec-env.std.com.free). + + +Description +----------- + +Overview +........ + +:mps:tag:`overview.access` The core MPS needs to access functionality that +could be provided by an ISO C hosted environment. + +:mps:tag:`overview.hosted` The core MPS must not make direct use of any +facilities in the hosted environment (design.mps.exec-env). However, +it is sensible to make use of them when the MPS is deployed in a +hosted environment. + +:mps:tag:`overview.hosted.indirect` The core MPS does not make any direct +use of hosted ISO C library facilities. Instead, it indirects through +the MPS Library Interface, impl.h.mpslib. + +:mps:tag:`overview.free` The core MPS can make direct use of freestanding +ISO C library facilities and does not need to include any of the +header files ````, ````, and ```` +directly. + +:mps:tag:`overview.complete` The MPS Library Interface can be considered as +the complete "interface to ISO" (in that it provides direct access to +facilities that we get in a freestanding environment and equivalents +of any functionality we require from the hosted environment). + +:mps:tag:`overview.provision.client` In a freestanding environment the +client is expected to provide functions meeting this interface to the +MPS. + +:mps:tag:`overview.provision.hosted` In a hosted environment, +impl.c.mpsliban may be used. It just maps impl.h.mpslib directly onto +the ISO C library equivalents. + +[missing diagram] + + +Outside the interface +..................... + +We provide impl.c.mpsliban to the client, for two reasons: + +1. the client can use it to connect the MPS to the ISO C library if it + exists; + +2. as an example implementation of the MPS Library Interface. + + +Implementation +-------------- + +:mps:tag:`impl` The MPS Library Interface comprises a header file +impl.h.mpslib and some documentation. + +:mps:tag:`impl.decl` The header file defines the interface to definitions +which parallel those parts of the non-freestanding ISO headers which +are used by the MPS. + +:mps:tag:`impl.include` The header file also includes the freestanding +headers ````, ````, and ```` (and not +````, though perhaps it should). + + diff --git a/mps/manual/html/_sources/design/lock.txt b/mps/manual/html/_sources/design/lock.txt index ceb782a14c1..d89df3d7788 100644 --- a/mps/manual/html/_sources/design/lock.txt +++ b/mps/manual/html/_sources/design/lock.txt @@ -1,6 +1,184 @@ +.. _design-lock: + + .. index:: pair: locking; design -.. _design-lock: -.. include:: ../../converted/lock.rst +The lock module +=============== + +.. mps:prefix:: design.mps.lock + + +Purpose +------- + +:mps:tag:`purpose` Support the locking needs of the thread-safe design. In +particular: + +- recursive locks; +- binary locks; +- recursive "global" lock that need not be allocated or initialized by + the client; +- binary "global" lock that need not be allocated or initialized by + the client. + +:mps:tag:`context` The MPS has to be able to operate in a multi-threaded +environment. The thread-safe design (design.mps.thread-safety) +requires client-allocatable binary locks, a global binary lock and a +global recursive lock. An interface to client-allocatable recursive +locks is also present to support any potential use, because of +historic requirements, and because the implementation will presumably +be necessary anyway for the global recursive lock. + + +Background +---------- + +:mps:tag:`need` In an environment where multiple threads are accessing +shared data. The threads which access data which is shared with other +threads need to cooperate with those threads to maintain consistency. +Locks provide a simple mechanism for doing this. + +:mps:tag:`ownership` A lock is an object which may be "owned" by a single +thread at a time. By claiming ownership of a lock before executing +some piece of code a thread can guarantee that no other thread owns +the lock during execution of that code. If some other thread holds a +claim on a lock, the thread trying to claim the lock will suspend +until the lock is released by the owning thread. + +:mps:tag:`data` A simple way of using this behaviour is to associate a lock +with a shared data structure. By claiming that lock around accesses to +the data, a consistent view of the structure can be seen by the +accessing thread. More generally any set of operations which are +required to be mutually exclusive may be performed so by using locks. + + +Overview +-------- + +:mps:tag:`adt` There is an abstract datatype :c:type:`Lock` which points to a +locking structure :c:type:`LockStruct`. This structure is opaque to any +client, although an interface is provided to supply the size of the +structure for any client wishing to make a new lock. The lock is not +allocated by the module as allocation itself may require locking. +:c:type:`LockStruct` is implementation specific. + +:mps:tag:`simple-lock` There are facilities for claiming and releasing +locks. :c:type:`Lock` is used for both binary and recursive locking. + +:mps:tag:`global-locks` "Global" locks are so called because they are used +to protect data in a global location (such as a global variable). The +lock module provides two global locks; one recursive and one binary. +There are facilities for claiming and releasing both of these locks. +These global locks have the advantage that they need not be allocated +or atomically initialized by the client, so they may be used for +locking the initialization of the allocator itself. The binary global +lock is intended to protect mutable data, possibly in conjunction with +other local locking strategies. The recursive global lock is intended +to protect static read-only data during one-off initialization. See +design.mps.thread-safety. + +:mps:tag:`deadlock` This module does not provide any deadlock protection. +Clients are responsible for avoiding deadlock by using traditional +strategies such as ordering of locks. (See +design.mps.thread-safety.deadlock.) + +:mps:tag:`single-thread` In the single-threaded configuration, locks are not +needed and the claim/release interfaces defined to be no-ops. + + +Detailed design +--------------- + +:mps:tag:`interface` The interface comprises the following functions: + +.. c:function:: size_t LockSize(void) + +Return the size of a :c:type:`LockStruct` for allocation purposes. + +.. c:function:: void LockInit(Lock lock) + +After initialisation the lock is not owned by any thread. + +.. c:function:: void LockFinish(Lock lock) + +Before finalisation the lock must not beowned by any thread. + +.. c:function:: void LockClaim(Lock lock) + +Claims ownership of a lock that was previously not held by current +thread. + +.. c:function:: void LockReleaseMPM(Lock lock) + +Releases ownership of a lock that is currently owned. + +.. c:function:: void LockClaimRecursive(Lock lock) + +Remembers the previous state of the lock with respect to the current +thread and claims the lock (if not already held). + +.. c:function:: void LockReleaseRecursive(Lock lock) + +Testores the previous state of the lock stored by corresponding +:c:func:`LockClaimRecursive()` call. + +.. c:function:: void LockClaimGlobal(void) + +Claims ownership of the binary global lock which was previously not +held by current thread. + +.. c:function:: void LockReleaseGlobal(void) + +Releases ownership of the binary global lock that is currently owned. + +.. c:function:: void LockClaimGlobalRecursive(void) + +Remembers the previous state of the recursive global lock with respect +to the current thread and claims the lock (if not already held). + +.. c:function:: void LockReleaseGlobalRecursive(void) + +Restores the previous state of the recursive global lock stored by +corresponding :c:func:`LockClaimGlobalRecursive()` call. + +:mps:tag:`impl.recursive` For recursive claims, the list of previous states +can be simply implemented by keeping a count of the number of claims +made by the current thread so far. In multi-threaded implementation +below this is handled by the operating system. A count is still kept +and used to check correctness. + +:mps:tag:`impl.global` The binary and recursive global locks may actually be +implemented using the same mechanism as normal locks. + +:mps:tag:`impl.ansi` Single-Threaded Generic Implementation: + +- single-thread; +- no need for locking; +- locking structure contains count; +- provides checking in debug version; +- otherwise does nothing except keep count of claims. + +:mps:tag:`impl.win32` Win32 Implementation: + +- supports Win32's threads; +- uses Critical Sections [ref?]; +- locking structure contains a Critical Section; +- both recursive and non-recursive calls use same Windows function; +- also performs checking. + +:mps:tag:`impl.linux` LinuxThreads Implementation (possibly suitable for all +PThreads implementations): + +- supports LinuxThreads threads, which are an implementation of + PThreads (see ``_); +- locking structure contains a mutex, initialized to check for + recursive locking; +- locking structure contains a count of the number of active claims; +- non-recursive locking calls :c:func:`pthread_mutex_lock()` and expects success; +- recursive locking calls :c:func:`pthread_mutex_lock()` and expects either + success or :c:macro:`EDEADLK` (indicating a recursive claim); +- also performs checking. + diff --git a/mps/manual/html/_sources/design/locus.txt b/mps/manual/html/_sources/design/locus.txt new file mode 100644 index 00000000000..3525778a872 --- /dev/null +++ b/mps/manual/html/_sources/design/locus.txt @@ -0,0 +1,669 @@ +.. _design-locus: + + +.. index:: + pair: locus manager; design + + +MPS Configuration +================= + +.. mps:prefix:: design.mps.locus + + +Introduction +------------ + +:mps:tag:`intro` The locus manager coordinates between the pools and takes +the burden of having to be clever about tract/group placement away +from the pools, preserving trace differentiability and contiguity +where appropriate. + +:mps:tag:`source` mail.gavinm.1998-02-05.17-52(0), mail.ptw.1998-02-05.19-53(0), +mail.pekka.1998-02-09.13-58(0), and mail.gavinm.1998-02-09.14-05(0). + +:mps:tag:`readership` Any MPS developer. + + +Overview +-------- + +The MPS manages three main resources: + +1. storage; +2. address space; +3. time. + +The locus manager manages address space at the arena level. + +.. note: + + Tucker was right: see mail.ptw.1998-11-02.14-25. Richard Kistruck, + 2007-04-24. + +When a pool wants some address space, it expresses some preferences to +the locus manager. The locus manager and the arena (working together) +try to honour these preferences, and decide what address space the +pool gets. + +Preferences are expressed by the ``SegPref`` argument to +:c:func:`SegAlloc()`. Note that, when they call :c:func:`SegAlloc()`, pools are +asking for address space and writeable storage simultaneously, in a +single call. There is currently no way for pools to reserve address +space without requesting storage. + + +Why is it important to manage address space? +............................................ + +1. Trace differentiability + + Carefully chosen addresses are used by reference tracing systems + (ie. automatic pools), to categorise objects into clumps; and to + summarise and cheaply find references between clumps. + + Different clumps will become worth collecting at different times + (the classic example, of course, is generations in a generational + collector). For these partial collections to be efficient, it must + be cheap to keep these clumps differentiable, cheap to condemn + (Whiten) a particular clump, and cheap to find a good conservative + approximation to all inward references to a clump (both initially + to construct the Grey set, and to make scanning the Grey set + efficient). + + This is what the MPS zone mechanism is all about. + + The locus manager manages the mapping from clumps to zones. + + To specify a clump, pools use the ``SegPrefGen`` argument to + :c:func:`SegPrefExpress()`. + + .. note:: + + The name is misleading: generations are only one sort of clump. + Richard Kistruck, 2007-04-24. + +2. Prevent address space fragmentation (within the arena) + + Address space is not infinite. + + In some use cases, the MPS is required to remain efficient when + using very nearly all available address space and storage. For + example, with the client-arena class, where the only address space + available is that of the storage available. + + Even with the VM arena class, typical storage sizes (as of 2007) + can make 32-bit address space constrained: the client may need + several gigabytes, which leaves little spare address space. + + Address space fragmentation incurs failure when there is no way to + allocate a big block of address space. The big block may be + requested via the MPS (by the client), or by something else in the + same process, such as a third-party graphics library, image + library, etc. + + Address space fragmentation incurs cost when: + + - desired large-block requests (such as for buffering) are denied, + causing them to be re-requested as a smaller block, or as several + smaller blocks; + + - possible operating-system costs in maintaining a fragmented + mapping? + +3. Prevent storage fragmentation (within tracts and segments) + + Storage is not infinite: it is allocated in multiples of a + fixed-size tract. Small lonely objects, each retaining a whole + tract, cause storage fragmentation. + + Non-moving pools manage this fragmentation with placement + strategies that use: + + - co-located death (in space and time); + - segment merging and splitting. + + These pool-level strategies always care about contiguity of object + storage. They also often care about the *ordering* of addresses, + because pool code uses an address-ordered search when choosing + where to place a new object. For these two reasons, the address + chosen (by the locus manager and arena) for new tracts is + important. + + Certain specialised pools, and/or some client programs that use + them, have carefully tuned segment sizes, positioning, and search + order. Be careful: seemingly inconsequential changes can + catastrophically break this tuning. + + Pools can specify a preference for High and Low ends of address + space, which implies a search-order. Pools could also specify + clumping, using either ``SegPrefGen`` or ``SegPrefZoneSet``. + + +Discovering the layout +...................... + +The locus manager is not given advance notice of how much address +space will be required with what preferences. Instead, the locus +manager starts with an empty layout, and adapts it as more requests +come in over time. It is attempting to discover a suitable layout by +successive refinement. This is ambitious. + + +Definitions +----------- + +:mps:tag:`note.cohort` We use the word "cohort" in its usual sense here, but +we're particularly interested in cohorts that have properties relevant +to tract placement. It is such cohorts that the pools will try to +organize using the services of the locus manager. Typical properties +would be trace differentiability or (en masse) death-time +predictability. Typical cohorts would be instances of a +non-generational pool, or generations of a collection strategy. + +:mps:tag:`def.trace.differentiability` Objects (and hence tracts) that are +collected, may or may not have "trace differentiability" from each +other, depending on their placement in the different zones. Objects +(or pointers to them) can also have trace differentiability (or not) +from non-pointers in ambiguous references; in practice, we will be +worried about low integers, that may appear to be in zones 0 or -1. + + +Requirements +------------ + +:mps:tag:`req.cohort` Tract allocations must specify the cohort they +allocate in. These kind of cohorts will be called loci, and they will +have such attributes as are implied by the other requirements. +Critical. + +:mps:tag:`req.counter.objects` As a counter-requirement, pools are expected +to manage objects. Objects the size of a tract allocation request +(segment-sized) are exceptional. Critical. +:mps:tag:`req.counter.objects.just` This means the locus manager is not +meant to solve the problems of allocating large objects, and it isn't +required to know what goes on in pools. + +:mps:tag:`req.contiguity` Must support a high level of contiguity within +cohorts when requested. This means minimizing the number of times a +cohort is made aware of discontiguity. Essential (as we've effectively +renegotiated this in SW, down to a vague hope that certain critical +cohorts are not too badly fragmented). :mps:tag:`req.contiguity.just` TSBA. + +:mps:tag:`req.contiguity.specific` It should be possible to request another +allocation next to a specific tract on either side (or an extension in +that direction, as the case may be). Such a request can fail, if +there's no space there. Nice. It would also be nice to have one for +"next to the largest free block". + +:mps:tag:`req.differentiable` Must support the trace differentiability of +segments that may be condemned separately. Due to the limited number +of zones, it must be possible to place several cohorts into the same +zone. Essential. + +:mps:tag:`req.differentiable.integer` It must be possible to place +collectable allocations so that they are trace-differentiable from +small integers. Essential. + +:mps:tag:`req.disjoint` Must support the disjointness of pages that have +different VM properties (such as mutable/immutable, +read-only/read-write, and different lifetimes). Optional. + +.. note:: + + I expect the implementation will simply work at page or larger + granularity, so the problem will not arise, but Tucker insisted on + stating this as a requirement. Pekka P. Pirinen, 1998-10-28. + +:mps:tag:`req.low-memory` The architecture of the locus manager must not +prevent the design of efficient applications that often use all +available memory. Critical. :mps:tag:`req.low-memory.expl` This basically +says it must be designed to perform well in low-memory conditions, but +that there can be configurations where it doesn't do as well, as long +as this is documented for the application programmer. Note that it +doesn't say all applications are efficient, only that if you manage to +design an otherwise efficient application, the locus manager will not +sink it. + +:mps:tag:`req.address` Must conserve address space in VM arenas to a +reasonable extent. Critical. + +:mps:tag:`req.inter-pool` Must support the association of sets of tracts in +different pools into one cohort. Nice. + +:mps:tag:`req.ep-style` Must support the existing EP-style of allocation +whereby allocation is from one end of address space either upwards or +downwards (or a close approximation thereto with the same behavior). +:mps:tag:`req.ep-style.just` We cannot risk disrupting a policy with +well-known properties when this technology is introduced. + +:mps:tag:`req.attributes` There should be a way to inform the locus manager +about various attributes of cohorts that might be useful for +placement: deathtime, expected total size, and so on. Optional. It's a +given that the cohorts must then have these attributes, within the +limits set in the contract of the appropriate interface. +:mps:tag:`req.attributes.action` The locus manager should use the attributes +to guide its placement decisions. Nice. + +:mps:tag:`req.blacklisting` There should be a way of maintaining at least +one blacklist for pages (or some other small unit), that can +not/should not be allocated to collectable pools. Optional. + +.. note:: + + How to do blacklist breaking for ambiguous refs? + +:mps:tag:`req.hysteresis` There should be a way to indicate which cohorts +fluctuate in size and by how much, to guide the arena hysteresis to +hold on to suitable pages. Optional. + + +Analysis +-------- + +:mps:tag:`anal.sw` Almost any placement policy would be an improvement on +the current SW one. + +:mps:tag:`anal.cause-and-effect` The locus manager doesn't usually need to +know *why* things need to be differentiable, disjoint, contiguous, and +so on. Abstracting the reason away from the interface makes it more +generic, more likely to have serendipitous new uses. Attributes +described by a quantity (deathtime, size, etc.) are an exception to +this, because we can't devise a common measure. + +:mps:tag:`anal.stable` The strategy must be stable: it must avoid repeated +recomputation, especially the kind that switches between alternatives +with a short period (repeated "bites" out the same region or +flip-flopping between two regions). + +:mps:tag:`anal.fragmentation` There's some call to avoid fragmentation in +cohorts that don't need strict contiguity, but this is not a separate +requirement, since fragmentation is a global condition, and can only +be ameliorated if there's a global strategy that clumps allocations +together. + +:mps:tag:`anal.deathtime` Cohorts with good death-time clumping of their +objects could use some locality of tract allocation, because it +increases the chances of creating large holes in the address space +(for other allocation to use). OTOH. many cohorts will not do multiple +frees in short succession, or at least cannot reasonably be predicted +to do so. This locality is not contiguity, nor is it low +fragmentation, it's just the requirement to place the new tracts next +to the tract where the last object was allocated in the cohort. Note +that the placement of objects is under the control of the pool, and +the locus manager will not know it, therefore this requirement should +be pursued by requesting allocation next to a particular tract (which +we already have a requirement for). + +:mps:tag:`anal.asymmetrical` The strategy has to be asymmetrical with +respect to cohorts growing and shrinking. The reason of this asymmetry +is that it can choose where to grow, but it cannot choose where to +shrink (except in a small way by growing with good locality). + + +Interface +--------- + +:mps:tag:`interface.locus` A cohort will typically reside on multiple tracts +(and the pools will avoid putting objects of other cohorts on them), +so there should be an interface to describe the properties of the +cohort, and associate each allocation request with the cohort. We +shall call such an object, created to represent a cohort, a locus (pl. +loci). + +:mps:tag:`interface.locus.pool` Loci will usually be created by the pool +that uses it. Some of the locus attributes will be inherited from +client-specified pool attributes [this means there will be additional +pool attributes]. + +:mps:tag:`interface.detail` This describes interface in overview; for +details, see implementation section and code, or user doc. + + +Loci +.... + +.. c:function:: Res LocusCreate(Locus *locusReturn, LocusAttrs attrs, ZoneGroup zg, LocusAllocDesc adesc) + +:mps:tag:`function.create` A function to create a locus: ``adesc`` contains +the information about the allocation sequences in the locus, ``zg`` is +used for zone differentiability, and ``attrs`` encodes the following: + +- :mps:tag:`locus.contiguity` A locus can be contiguous. This means + performing as required in :mps:ref:`.req.contiguity`, non-contiguous + allocations can be freely placed anywhere (but efficiency dictates + that similar allocations are placed close together and apart from + others). + +- :mps:tag:`locus.blacklist` Allocations in the locus will avoid blacklisted + pages (for collectable segments). + +- :mps:tag:`locus.zero` Allocations in the locus are zero-filled. + +.. note:: + + Other attributes will be added, I'm sure. + +:mps:tag:`interface.zone-group` The locus can be made a member of a zone +group. Passing ``ZoneGroupNONE`` means it's not a member of any group +(allocations will be placed without regard to zone, except to keep +them out of stripes likely to be needed for some group). + +.. note:: + + I propose no mechanism for managing zone groups at this time, + since it's only used internally for one purpose. Pekka P. Pirinen, + 2000-01-17. + +:mps:tag:`interface.size` An allocation descriptor (``LocusAllocDesc``) +contains various descriptions of how the locus will develop over time +(inconsistent specifications are forbidden, of course): + +- :mps:tag:`interface.size.typical-alloc` Size of a typical allocation in + this locus, in bytes. This will mainly affect the grouping of + non-contiguous loci. + +- :mps:tag:`interface.size.large-alloc` Typical large allocation that the + manager should try to allow for (this allows some relief from + :mps:ref:`.req.counter.objects`), in bytes. This will mainly affect the size + of gaps that will be allotted adjoining this locus. + +- :mps:tag:`interface.size.direction` Direction of growth: up/down/none. + Only useful if the locus is contiguous. + +- :mps:tag:`interface.size.lifetime` Some measure of the lifetime of tracts + (not objects) in the cohort. + + .. note:: + + Don't know the details yet, probably only useful for placing + similar cohorts next to each other, so the details don't + actually matter. Pekka P. Pirinen, 2000-01-17. + +- :mps:tag:`interface.size.deathtime` Some measure of the deathtime of + tracts (not objects) in the cohort. + + .. note:: + + Ditto. Pekka P. Pirinen, 2000-01-17. + +:mps:tag:`function.init` :c:func:`LocusInit()` is like :c:func:`LocusCreate()`, but +without the allocation. This is the usual interface, since most loci +are embedded in a pool or something. + +:mps:tag:`function.alloc` :c:func:`ArenaAlloc()` to take a locus argument. +:c:func:`ArenaAllocHere()` is like it, plus it takes a tract and a +specification to place the new allocation immediately above/below a +given tract; if that is not possible, it returns ``ResFAIL`` (this +will make it useful for reallocation functionality). + +.. c:function:: ArenaSetTotalLoci(Arena arena, Size nLoci, Size nZoneGroups) + +:mps:tag:`function.set-total` A function to tell the arena the expected +number of (non-miscible client) loci, and of zone groups. + + +Peaks +..... + +.. c:function:: mps_res_t mps_peak_create(mps_peak_t*, mps_arena_t) + +:mps:tag:`function.peak.create` A function to create a peak. A newly-created +peak is open, and will not be used to guide the strategy of the locus +manager. + +.. c:function:: mps_res_t mps_peak_describe_pool(mps_peak_t, mps_pool_t, mps_size_desc_t) + +:mps:tag:`function.peak.add` A function to add a description of the state of +one pool into the peak. Calling this function again for the same peak and pool instance will replace +the earlier description. + +:mps:tag:`function.peak.add.size` The size descriptor contains a total size +in bytes or percent of arena size. + +.. note:: + + Is this right? Pekka P. Pirinen, 2000-01-17. + +:mps:tag:`function.peak.add.remove` Specifying a :c:macro:`NULL` size will remove +the pool from the peak. The client is not allowed to destroy a pool +that is mentioned in any peak; it must be first removed from the peak, +or the peak must be destroyed. This is to ensure that the client +adjusts the peaks in a manner that makes sense to the application; the +locus manager can't know how to do that. + +.. c:function:: mps_res_t mps_peak_close(mps_peak_t) + +:mps:tag:`function.peak.close` A function to indicate that all the +significant pools have been added to the peak, and it can now be used +to guide the locus manager. For any pool not described in the peak, +the locus manager will take its current size at any given moment as +the best prediction of its size at the peak. + +:mps:tag:`function.peak.close.after` It is legal to add more descriptions to +the peak after closing, but this will reopen the peak, and it will +have to be closed before the locus manager will use it again. The +locus manager uses the previous closed state of the peak, while this +is going on. + +.. c:function:: void mps_peak_destroy(mps_peak_t) + +:mps:tag:`function.peak.destroy` A function to destroy a peak. + +:mps:tag:`interface.ep-style` This satisfies :mps:ref:`.req.ep-style` by allowing SW +to specify zero size for most pools (which will cause them to be place +next to other loci with the same growth direction). + +.. note:: + + Not sure this is good enough, but we'll try it first. Pekka P. + Pirinen, 2000-01-17. + + +Architecture +------------ + +Data objects +............ + +:mps:tag:`arch.locus` To represent the cohorts, we have locus objects. +Usually a locus is embedded in a pool instance, but generations are +separate loci. + +:mps:tag:`arch.locus.attr` contiguity, blacklist, zg, current region, @@@@ + +:mps:tag:`arch.locus.attr.exceptional` The client can define a typical large +allocation for the locus. Requests substantially larger than that are +deemed exceptional. + +:mps:tag:`arch.zone-group` To satisfy :mps:ref:`.req.condemn`, we offer zone groups. +Each locus can be a member of a zone group, and the locus manager will +attempt to place allocations in this locus in different zones from all +the other zone groups. A zone-group is represented as @@@@. + +:mps:tag:`arch.page-table` A page table is maintained by the arena, as usual +to track association between tracts, pools and segments, and mapping +status for VM arenas. + +:mps:tag:`arch.region` All of the address space is divided into disjoint +regions, represented by region objects. These objects store their +current limits, and high and low watermarks of currently allocated +tracts (we hope there's usually a gap of empty space between regions). +The limits are actually quite porous and flexible. + +:mps:tag:`arch.region.assoc` Each region is associated with one contiguous +locus or any number of non-contiguous loci (or none). We call the +first kind of region "contiguous". :mps:tag:`arch.locus.assoc` Each locus +remembers all regions where it has tracts currently, excepting the +badly-placed allocations (see below). It is not our intention that any +locus would have very many, or that loci that share regions would have +any reason to stop doing do. + +:mps:tag:`arch.region.more` Various quantities used by the placement +computation are also stored in the regions and the loci. Regions are +created (and destroyed) by the placement recomputation. Regions are +located in stripes (if it's a zoned region), but they can extend into +neighboring stripes if an exceptionally large tract allocation is +requested (to allow for large objects). + +:mps:tag:`arch.chunk` Arenas may allocate more address space in additional +chunks, which may be disjoint from the existing chunks. Inter-chunk +space will be represented by dummy regions. There are also sentinel +regions at both ends of the address space. + + +Overview of strategy +.................... + +:mps:tag:`arch.strategy.delay` The general strategy is to delay placement +decisions until they have to be made, but no later. + +:mps:tag:`arch.strategy.delay.until` Hence, the locus manager only makes +placement decisions when an allocation is requested (frees and other +operations might set a flag to cause the next allocation to redecide). +This also allows the client to change the peak and pool configuration +in complicated ways without causing a lot of recomputation, by doing +all the changes without allocating in the middle (unless the control +pool needs more space because of the changes). + +:mps:tag:`arch.strategy.normal` While we want the placement to be +sophisticated, we do not believe it is worth the effort to consider +all the data at each allocation. Hence, allocations are usually just +placed in one of the regions used previously (see :mps:ref:`.arch.alloc`) +without reconsidering the issues. + +:mps:tag:`arch.strategy.normal.limit` However, the manager sets +precautionary limits on the regions to ensure that the placement +decisions are revisited when an irrevocable placement is about to be +made. + +:mps:tag:`arch.strategy.create` The manager doesn't create new regions until +they are needed for allocation (but it might compute where they could +be placed to accommodate a peak). + + +Allocation +.......... + +:mps:tag:`arch.alloc` Normally, each allocation to a locus is placed in its +current region. New regions are only sought when necessary to fulfill +an allocation request or when there is reason to think the situation +has changed significantly (see :mps:ref:`.arch.significant`). + +:mps:tag:`arch.alloc.same` An allocation is first attempted next to the +previous allocation in the same locus, respecting growth direction. If +that is not possible, a good place in the current region is sought. +:mps:tag:`arch.alloc.same.hole` At the moment, for finding a good place +within a region, we just use the current algorithm, limited to the +region. In future, the placement within regions will be more clever. + +:mps:tag:`arch.alloc.extend` If there's no adequate hole in the current +region and the request is not exceptional, the neighboring regions are +examined to see if the region could be extended at one border. (This +will basically only be done if the neighbor has shrunk since the last +placement recomputation, because the limit was set on sophisticated +criteria, and should not be changed without justification.) +:mps:tag:`arch.alloc.extend.here` When an allocation is requested next to a +specific tract (:c:func:`ArenaAllocHere()`), we try to extend a little +harder (at least for ``change_size``, perhaps not for locality). + +:mps:tag:`arch.alloc.other` If no way can be found to allocate in the +current region, other regions used for this locus are considered in +the same way, to see if space can be found there. [Or probably look at +other regions before trying to extend anything?] + +:mps:tag:`arch.alloc.recompute` When no region of this locus has enough +space for the request, or when otherwise required, region placement is +recomputed to find a new region for the request (which might be the +same region, after extension). + +:mps:tag:`arch.alloc.current` This region where the allocation was placed +then becomes the current region for this locus, except when the +request was exceptional, or when the region chosen was "bad" (see +@@@@). + +:mps:tag:`arch.significant` Significant changes to the parameters affecting +placement are deemed to have happened at certain client calls and when +the total allocation has changed substantially since the last +recomputation. Such conditions set a flag that causes the next +allocation to recompute even if its current region is not full +(possibly second-guess the decision to recompute after some +investigation of the current state?). + + +Deallocation +............ + +:mps:tag:`arch.free` Deallocation simply updates the counters in the region +and the locus. For some loci, it will make the region of the +deallocation the current region. :mps:tag:`arch.free.remove` If a region +becomes entirely empty, it is deleted (and the neighbors limits might +be adjusted). + +.. note:: + + This is quite tricky to get right. + + +Region placement recomputation +.............................. + +:mps:tag:`arch.gap` When doing placement computations, we view the arena as +a sequence of alternating region cores and gaps (which can be small, +even zero-sized). Initially, we'll take the core of a region to be the +area between the high and low watermark, but in the future we might be +more flexible about that. + +.. note:: + + Edge determination is actually a worthwhile direction to explore. + +:mps:tag:`arch.reach` The gap between two cores could potentially end up +being allocated to either region, if they grow in that direction, or +one or neither, if they don't. The set of states that the region +assignment could reach by assigning the gaps to their neighbors is +called the reach of the current configuration. + +:mps:tag:`arch.placement.object` The object of the recomputation is to find +a configuration of regions that is not too far from the current +configuration and that keeps all the peaks inside its reach; if that +is not possible, keep the nearest ones in the reach and then minimize +the total distance from the rest. + +:mps:tag:`arch.placement.hypothetical` The configurations that are +considered will include hypothetical placements for new regions for +loci that cannot fit in their existing regions at the peak. This is +necessary to avoid choosing a bad alternative. + +:mps:tag:`arch.placement.interesting` The computation will only consider new +regions of loci that are deemed interesting, that is, far from their +peak state. This will reduce the computational burden and avoid +jittering near a peak. + +.. note:: + + Details missing. + + +Implementation +-------------- + +[missing] + + +Notes +----- + +:mps:tag:`idea.change` Even after the first segment, be prepared to change +your mind, if by the second segment a lot of new loci have been +created. + +:mps:tag:`distance` If the current state is far from a peak, there's time to +reassign regions and for free space to appear (in fact, under the +steady arena assumption, enough free space *will* appear). + +:mps:tag:`clear-pool` Need to have a function to deallocate all objects in a +pool, so that :c:func:`PoolDestroy()` won't have to be used for that +purpose. + + diff --git a/mps/manual/html/_sources/design/message-gc.txt b/mps/manual/html/_sources/design/message-gc.txt new file mode 100644 index 00000000000..e0bbead9427 --- /dev/null +++ b/mps/manual/html/_sources/design/message-gc.txt @@ -0,0 +1,292 @@ +.. _design-message-gc: + + +.. index:: + pair: garbage collection messages; design + + +GC messages +=========== + +.. mps:prefix:: design.mps.config + + +Introduction +------------ + +:mps:tag:`intro` This document describes the design of the MPS garbage +collection messages. For a guide to the MPS message system in general, +see design.mps.message. + +:mps:tag:`readership` Any MPS developer. + + +Overview +-------- + +The MPS provides two types of GC messages: + +- :c:func:`mps_message_type_gc_start()`; +- :c:func:`mps_message_type_gc()`. + +They are called "trace start" and "trace end" messages in this +document and in most MPS source code. + + +Introduction +------------ + +The MPS posts a trace start message (:c:func:`mps_message_type_gc_start()`) +near the start of every trace (but after calculating the condemned +set, so we can report how large it is). + +The MPS posts a trace end message (:c:func:`mps_message_type_gc()`) near the +end of every trace. + +These messages are extremely flexible: they can hold arbitrary +additional data simply by writing new accessor functions. If there is +more data to report at either of these two events, then there is a +good argument for adding it into these existing messages. + +.. note:: + + In previous versions of this design document, there was a partial + unimplemented design for an :c:func:`mps_message_type_gc_generation()` + message. This would not have been a good design, because managing + and collating multiple messages is much more complex for both MPS + and client than using a single message. Richard Kistruck, + 2008-12-19. + + +Purpose +------- + +:mps:tag:`purpose` The purpose of these messages is to allow the client +program to be aware of GC activity, in order to: + +- adjust its own behaviour programmatically; +- show or report GC activity in a custom way, such as an in-client + display, in a log file, etc. + +The main message content should be intelligible and helpful to +client-developers (with help from MPS staff if necessary). There may +be extra content that is only meaningful to MPS staff, to help us +diagnose client problems. + +While there is some overlap with the Diagnostic Feedback system +(design.mps.diag), the main contrasts are that these GC messages are +present in release builds, are stable from release to release, and are +designed to be parsed by the client program. + + +Names and parts +--------------- + +Here's a helpful list of the names used in the GC message system: + +Implementation is mostly in the source file ``traceanc.c`` (trace +ancillary). + +============================= ============================== ====================== +Internal name "trace start" "trace end" +Internal type ``TraceStartMessage`` ``TraceMessage`` +:c:type:`ArenaStruct` member ``tsMessage[]`` ``tMessage`` +Message type ``MessageTypeGCSTART`` ``MessageTypeGC`` +External name ``mps_message_type_gc_start`` ``mps_message_type_gc`` +============================= ============================== ====================== + +.. note:: + + The names of these messages are unconventional; they should + properly be called "gc (or trace) *begin*" and "gc (or trace) + *end*". But it's much too late to change them now. Richard + Kistruck, 2008-12-15. + +Collectively, the trace-start and trace-end messages are called the +"trace id messages", and they are managed by the functions +:c:func:`TraceIdMessagesCheck()`, :c:func:`TraceIdMessagesCreate()`, and :c:func:`TraceIdMessagesDestroy()`. + +The currently supported message-field accessor methods are: +:c:func:`mps_message_gc_start_why()`, :c:func:`mps_message_gc_live_size()`, +:c:func:`mps_message_gc_condemned_size()`, and +:c:func:`mps_message_gc_not_condemned_size()`. These are documented in the +Reference Manual. + + +Lifecycle +--------- + +:mps:tag:`lifecycle` for each trace id, pre-allocate a pair of start/end +messages by calling :c:func:`ControlAlloc()`. Then, when a trace runs using +that trace id, fill in and post these messages. As soon as the trace +has posted both messages, immediately pre-allocate a new pair of +messages, which wait in readiness for the next trace to use that +trace id. + + +Requirements +............ + +:mps:tag:`req.no-start-alloc` Should avoid attempting to allocate memory at +trace start time. :mps:tag:`req.no-start-alloc.why` There may be no free +memory at trace start time. Client would still like to hear about +collections in those circumstances. + +:mps:tag:`req.queue` Must support a client that enables, but does not +promptly retrieve, GC messages. Messages that have not yet been +retrived must remain queued, and the client must be able to retrieve +them later without loss. It is not acceptable to stop issuing GC +messages for subsequent collections merely because messages from +previous collections have not yet been retrieved. :mps:tag:`req.queue.why` +This is because there is simply no reasonable way for a client to +guarantee that it always promptly collects GC messages. + +:mps:tag:`req.match` Start and end messages should always match up: never +post one of the messages but fail to post the matching one. + +:mps:tag:`req.match.why` This makes client code much simpler -- it does not +have to handle mismatching messages. + +:mps:tag:`req.errors-not-direct` Errors (such as a :c:func:`ControlAlloc()` +failure) cannot be reported directly to the client, because +collections often happen automatically, without an explicit client +call to the MPS interface. + +:mps:tag:`req.multi-trace` Up to ``TraceLIMIT`` traces may be running, and +emitting start/end messages, simultaneously. + +:mps:tag:`req.early` Nice to tell client as much as possible about the +collection in the start message, if we can. + +:mps:tag:`req.similar` Start and end messages are conceptually similar -- it +is quite okay, and may be helpful to the client, for the same datum +(for example: the reason why the collection occurred) to be present in +both the start and end message. + + +Storage +....... + +For each trace-id (:mps:ref:`.req.multi-trace`) a pair (:mps:ref:`.req.match`) of +start/end messages is dynamically allocated (:mps:ref:`.req.queue`) in advance +(:mps:ref:`.req.no-start-alloc`). Messages are allocated in the control pool +using :c:func:`ControlAlloc()`. + +.. note:: + + Previous implementations of the trace start message used static + allocation. This does not satisfy :mps:ref:`.req.queue`. See also + job001570_. Richard Kistruck, 2008-12-15. + + .. _job001570: http://www.ravenbrook.com/project/mps/issue/job001570/ + +Poiters to these messages are stored in ``tsMessage[ti]`` and +``tMessage[ti]`` arrays in the :c:type:`ArenaStruct`. + +.. note:: + + We must not> keep the pre-allocated messages, or pointers to them, + in :c:type:`TraceStruct`: the memory for these structures is statically + allocated, but the values in them are re-initialised by + :c:func:`TraceCreate()` each time the trace id is used, so the + :c:func:`TraceStruct()` is invalid (that is: to be treated as random + uninitialised memory) when not being used by a trace. See also + job001989_. Richard Kistruck, 2008-12-15. + + .. _job001989: http://www.ravenbrook.com/project/mps/issue/job001989/ + + +Creating and Posting +.................... + +In :c:func:`ArenaCreate()` we use :c:macro:`TRACE_SET_ITER` to initialise the +``tsMessage[ti]`` and ``tMessage[ti]`` pointers to :c:macro:`NULL`, and then +(when the control pool is ready) :c:macro:`TRACE_SET_ITER` calling +:c:func:`TraceIdMessagesCreate()`. This performs the initial pre-allocation +of the trace start/end messages for each trace id. Allocation failure +is not tolerated here: it makes :c:func:`ArenaCreate()` fail with an error +code, because the arena is deemed to be unreasonably small. + +When a trace is running using trace id ``ti``, it finds a +pre-allocated message via ``tsMessage[ti]`` or ``tMessage[ti]`` in the +:c:func:`ArenaStruct()`, fills in and posts the message, and nulls-out the +pointer. (If the pointer was null, no message is sent; see below.) The +message is now reachable only from the arena message queue (but the +control pool also knows about it). + +When the trace completes, it calls :c:func:`TraceIdMessagesCreate()` for its +trace id. This performs the ongoing pre-allocation of the trace +start/end messages for the next use of this trace id. The expectation +is that, after a trace has completed, some memory will have been +reclaimed, and the :c:func:`ControlAlloc()` will succeed. + +But allocation failure here is permitted: if it happens, both the +start and the end messages are freed (if present). This means that, +for the next collection using this trace id, neither a start nor an +end message will be sent (:mps:ref:`.req.match`). There is no direct way to +report this failure to the client (:mps:ref:`.req.errors-not-direct`), so we +just increment the ``droppedMessages`` counter in the :c:type:`ArenaStruct`. +Currently this counter is never reported to the client (except in +diagnostic varieties). + + +Getting and discarding +...................... + +If the client has not enabled that message type, the message is +discarded immediately when posted, calling :c:func:`ControlFree()` and +reclaiming the memory. + +If the client has enabled but never gets the message, it remains on +the message queue until :c:func:`ArenaDestroy()`. Theoretically these +messages could accumulate forever until they exhaust memory. This is +intentional: the client should not enable a message type and then +never get it! + +Otherwise, when the client gets a message, it is dropped from the +arena message queue: now only the client (and the control pool) hold +references to it. The client must call :c:func:`mps_message_discard()` once +it has finished using the message. This calls :c:func:`ControlFree()` and +reclaims the memory. + +If the client simply drops its reference, the memory will not be +reclaimed until :c:func:`ArenaDestroy()`. This is intentional: the control +pool is not garbage-collected. + + +Final clearup +............. + +Final clearup is performed at :c:func:`ArenaDestroy()`, as follows: + +- Unused and unsent pre-allocated messages (one per trace id) are + freed with :c:macro:`TRACE_SET_ITER` calling :c:func:`TraceIdMessagesDestroy()` + which calls the message Delete functions (and thereby + :c:func:`ControlFree()`) on anything left in ``tsMessage[ti]`` and + ``tMessage[ti]``. + +- Unretrieved messages are freed by emptying the arena message queue + with :c:func:`MessageEmpty()`. + +- Retrieved but undiscarded messages are freed by destroying the + control pool. + + +Testing +------- + +The main test is "``zmess.c``". See notes there. + +Various other tests, including ``amcss.c``, also collect and report +:c:func:`mps_message_type_gc()` and :c:func:`mps_message_type_gc_start()`. + + +Coverage +........ + +Current tests do not check: + +- The less common why-codes (reasons why a trace starts). These should + be added to ``zmess.c``. + + diff --git a/mps/manual/html/_sources/design/message.txt b/mps/manual/html/_sources/design/message.txt index 12413437cbd..c46909e8b69 100644 --- a/mps/manual/html/_sources/design/message.txt +++ b/mps/manual/html/_sources/design/message.txt @@ -1,7 +1,374 @@ +.. _design-message: + + .. index:: pair: messages; design single: client message protocol -.. _design-message: -.. include:: ../../converted/message.rst +Client message protocol +======================= + +.. mps:prefix:: design.mps.message + pair: messages; design + single: client message protocol + + +Introduction +------------ + +:mps:tag:`intro` The client message protocol provides a means by which +clients can receive messages from the MPS asynchronously. Typical +messages may be low memory notification (or in general low utility), +finalization notification, soft-failure notification. There is a +general assumption that it should not be disastrous for the MPS client +to ignore messages, but that it is probably in the clients best +interest to not ignore messages. The justification for this is that +the MPS cannot force the MPS client to read and act on messages, so no +message should be critical. + +.. note:: + + Bogus, since we cannot force clients to check error codes either. + Pekka P. Pirinen, 1997-09-17. + +:mps:tag:`contents` This document describes the design of the external and +internal interfaces and concludes with a sketch of an example design +of an internal client. The example is that of implementing +finalization using PoolMRG. + +:mps:tag:`readership` Any MPS developer. + + +Requirements +------------ + +:mps:tag:`req` The client message protocol will be used for implementing +finalization (see design.mps.finalize and req.dylan.fun.final). It +will also be used for implementing the notification of various +conditions (possibly req.dylan.prot.consult is relevant here). + + +External interface +------------------ + +:mps:tag:`if.queue` Messages are presented as a single queue per arena. +Various functions are provided to inspect the queue and inspect +messages in it (see below). + + +Functions +......... + +:mps:tag:`if.fun` The following functions are provided: + +:mps:tag:`if.fun.poll` :c:func:`mps_message_poll()` sees whether there are any +messages pending. Returns 1 only if there is a message on the queue of +arena. Returns 0 otherwise. + +:mps:tag:`if.fun.enable` :c:func:`mps_message_type_enable()` enables the flow of +messages of a certain type. The queue of messages of a arena will +contain only messages whose types have been enabled. Initially all +message types are disabled. Effectively this function allows the +client to declare to the MPS what message types the client +understands. The MPS does not generate any messages of a type that +hasn't been enabled. This allows the MPS to add new message types (in +subsequent releases of a memory manager) without confusing the client. +The client will only be receiving the messages if they have explicitly +enabled them (and the client presumably only enables message types +when they have written the code to handle them). + +:mps:tag:`if.fun.disable` :c:func:`mps_message_type_disable()` disables the flow +of messages of a certain type. The antidote to +:c:func:`mps_message_type_enable()`. Disables the specified message type. +Flushes any existing messages of that type on the queue, and stops any +further generation of messages of that type. This permits clients to +dynamically decline interest in a message type, which may help to +avoid a memory leak or bloated queue when the messages are only +required temporarily. + +:mps:tag:`if.fun.get` :c:func:`mps_message_get()` begins a message "transaction". +If there is a message of the specified type on the queue then the +first such message will be removed from the queue and a handle to it +will be returned to the client via the ``messageReturn`` argument; in +this case the function will return :c:macro:`TRUE`. Otherwise it will return +:c:macro:`FALSE`. Having obtained a handle on a message in this way, the +client can use the type-specific accessors to find out about the +message. When the client is done with the message the client should +call :c:func:`mps_message_discard()`; failure to do so will result in a +resource leak. + +:mps:tag:`if.fun.discard` :c:func:`mps_message_discard()` ends a message +"transaction". It indicates to the MPS that the client is done with +this message and its resources may be reclaimed. + +:mps:tag:`if.fun.type.any` :c:func:`mps_message_queue_type()` determines the type +of a message in the queue. Returns :c:macro:`TRUE` only if there is a message +on the queue of arena, and in this case updates the ``typeReturn`` +argument to be the type of a message in the queue. Otherwise returns +:c:macro:`FALSE`. + +:mps:tag:`if.fun.type` :c:func:`mps_message_type()` determines the type of a +message (that has already been got). Only legal when inside a message +transaction (that is, after :c:func:`mps_message_get()` and before +:c:func:`mps_message_discard()`). Note that the type will be the same as the +type that the client passed in the call to :c:func:`mps_message_get()`. + + +Types of messages +................. + +:mps:tag:`type` The type governs the "shape" and meaning of the message. + +:mps:tag:`type.int` Types themselves will just be a scalar quantity, an +integer. + +:mps:tag:`type.semantics` A type indicates the semantics of the message. + +:mps:tag:`type.semantics.interpret` The semantics of a message are +interpreted by the client by calling various accessor methods on the +message. + +:mps:tag:`type.accessor` The type of a message governs which accessor +methods are legal to apply to the message. + +:mps:tag:`type.example` Some example types: + +:mps:tag:`type.finalization` There will be a finalization type. The type is +abstractly: ``FinalizationMessage(Ref)``. + +:mps:tag:`type.finalization.semantics` A finalization message indicates that +an object has been discovered to be finalizable (see +design.mps.poolmrg.def.final.object for a definition of finalizable). + +:mps:tag:`type.finalization.ref` There is an accessor to get the reference +of the finalization message (i.e. a reference to the object which is +finalizable) called :c:func:`mps_message_finalization_ref()`. + +:mps:tag:`type.finalization.ref.scan` Note that the reference returned +should be stored in scanned memory. + + +Compatibility issues +.................... + +:mps:tag:`compatibility` The following issues affect future compatibility of +the interface: + +:mps:tag:`compatibility.future.type-new` Notice that message of a type that +the client doesn't understand are not placed on the queue, therefore +the MPS can introduce new types of message and existing client will +still function and will not leak resources. This has been achieved by +getting the client to declare the types that the client understands +(with :c:func:`mps_message_type_enable()`, :mps:ref:`.if.fun.enable`). + +:mps:tag:`compatibility.future.type-extend` The information available in a +message of a given type can be extended by providing more accessor +methods. Old clients won't get any of this information but that's +okay. + + +Internal interface +------------------ + +Types +..... + +.. c:type:: struct MessageStruct *Message + +:mps:tag:`message.type` :c:type:`Message` is the type of messages. + +:mps:tag:`message.instance` Messages are instances of Message Classes. + +.. c:type:: struct MessageStruct *MessageStruct + +:mps:tag:`message.concrete` Concretely a message is represented by a +:c:type:`MessageStruct`. A :c:type:`MessageStruct` has the usual signature field +(see design.mps.sig). A :c:type:`MessageStruct` has a type field which +defines its type, a ring node, which is used to attach the message to +the queue of pending messages, a class field, which identifies a +:c:type:`MessageClass` object. + +:mps:tag:`message.intent` The intention is that a :c:type:`MessageStruct` will be +embedded in some richer object which contains information relevant to +that specific type of message. + +:mps:tag:`message.struct` The structure is declared as follows:: + + struct MessageStruct { + Sig sig; + MessageType type; + MessageClass class; + RingStruct node; + } MessageStruct; + + +.. c:type:: struct MessageClassStruct *MessageClass + +:mps:tag:`class` A message class is an encapsulation of methods. It +encapsulates methods that are applicable to all types of messages +(generic) and methods that are applicable to messages only of a +certain type (type-specific). + +:mps:tag:`class.concrete` Concretely a message class is represented by a +:c:type:`MessageClassStruct` (a struct). Clients of the Message module are +expected to allocate storage for and initialise the +:c:type:`MessageClassStruct`. It is expected that such storage will be +allocated and initialised statically. + +:mps:tag:`class.one-type` A message class implements exactly one message +type. The identifier for this type is stored in the ``type`` field of +the :c:type:`MessageClassStruct`. Note that the converse is not true: a +single message type may be implemented by two (or more) different +message classes (for example: for two pool classes that require +different implementations for that message type). + +:mps:tag:`class.methods.generic` The generic methods are as follows: + +* ``delete`` -- used when the message is destroyed (by the client + calling :c:func:`mps_message_discard()`). The class implementation should + finish the message (by calling :c:func:`MessageFinish()`) and storage for + the message should be reclaimed (if applicable). + +:mps:tag:`class.methods.specific` The type specific methods are: + +:mps:tag:`class.methods.specific.finalization` Specific to +``MessageTypeFinalization``: + +* ``finalizationRef`` -- returns a reference to the finalizable object + represented by this message. + +:mps:tag:`class.methods.specific.collectionstats` Specific to ``MessageTypeCollectionStats``: + +* ``collectionStatsLiveSize`` -- returns the number of bytes (of + objects) that were condemned but survived. + +* ``collectionStatsCondemnedSize`` -- returns the number of bytes + condemned in the collection. + +* ``collectionStatsNotCondemnedSize`` -- returns the the number of + bytes (of objects) that are subject to a GC policy (that is, + collectable) but were not condemned in the collection. + +:mps:tag:`class.sig.double` The :c:type:`MessageClassStruct` has a signature field +at both ends. This is so that if the :c:type:`MessageClassStruct` changes +size (by adding extra methods for example) then any static +initializers will generate errors from the compiler (there will be a +type error causes by initialising a non-signature type field with a +signature) unless the static initializers are changed as well. + +:mps:tag:`class.struct` The structure is declared as follows:: + + typedef struct MessageClassStruct { + Sig sig; /* design.mps.sig */ + const char *name; /* Human readable Class name */ + + /* generic methods */ + MessageDeleteMethod delete; /* terminates a message */ + + /* methods specific to MessageTypeFinalization */ + MessageFinalizationRefMethod finalizationRef; + + /* methods specific to MessageTypeCollectionStats */ + MessageCollectionStatsLiveSizeMethod collectionStatsLiveSize; + MessageCollectionStatsCondemnedSizeMethod collectionStatsCondemnedSize; + MessageCollectionStatsNotCondemnedSizeMethod collectionStatsNotCondemnedSize; + + Sig endSig; /* design.mps.message.class.sig.double */ + } MessageClassStruct; + + +:mps:tag:`space.queue` The arena structure is augmented with a structure for +managing for queue of pending messages. This is a ring in the +:c:type:`ArenaStruct`:: + + struct ArenaStruct + { + ... + RingStruct messageRing; + ... + } + + +Functions +......... + +.. c:function:: void MessageInit(Arena arena, Message message, MessageClass class) + +:mps:tag:`fun.init` Initializes the :c:type:`MessageStruct` pointed to by +``message``. The caller of this function is expected to manage the +store for the :c:type:`MessageStruct`. + +.. c:function:: void MessageFinish(Message message) + +:mps:tag:`fun.finish` Finishes the :c:type:`MessageStruct` pointed to by +``message``. The caller of this function is expected to manage the +store for the :c:type:`MessageStruct`. + +.. c:function:: void MessagePost(Arena arena, Message message) + +:mps:tag:`fun.post` Places a message on the queue of an arena. + +:mps:tag:`fun.post.precondition` Prior to calling the function, the node +field of the message must be a singleton. After the call to the +function the message will be available for MPS client to access. After +the call to the function the message fields must not be manipulated +except from the message's class's method functions (that is, you +mustn't poke about with the node field in particular). + +.. c:function:: void MessageEmpty(Arena arena) + +:mps:tag:`fun.empty` Empties the message queue. This function has the same +effect as discarding all the messages on the queue. After calling this +function there will be no messages on the queue. + +:mps:tag:`fun.empty.internal-only` This functionality is not exposed to +clients. We might want to expose this functionality to our clients in +the future. + + +Message life cycle +------------------ + +:mps:tag:`life` A message will be allocated by a client of the message +module, it will be initialised by calling :c:func:`MessageInit()`. The +client will eventually post the message on the external queue (in fact +most clients will create a message and then immediately post it). The +message module may then apply any of the methods to the message. The +message module will eventually destroy the message by applying the +``delete`` method to it. + + +Examples +-------- + +Finalization +............ + +.. note:: + + Possibly out of date, see design.mps.finalize and + design.mps.poolmrg instead. David Jones, 1997-08-28. + +This subsection is a sketch of how PoolMRG will use Messages for +finalization (see design.mps.poolmrg). + +PoolMRG has guardians (see design.mps.poolmrg.guardian). Guardians are +used to manage final references and detect when an object is +finalizable. + +The link part of a guardian will include a :c:type:`MessageStruct`. + +The :c:type:`MessageStruct` is allocated when the final reference is created +(which is when the referred to object is registered for finalization). +This avoids allocating at the time when the message gets posted (which +might be a tricky, undesirable, or impossible, time to allocate). + +PoolMRG has two queues: the entry queue, and the exit queue. The entry +queue will use a ring; the exit queue of MRG will simply be the +external message queue. + +The ``delete`` method frees both the link part and the reference part +of the guardian. + + diff --git a/mps/manual/html/_sources/design/object-debug.txt b/mps/manual/html/_sources/design/object-debug.txt index 0c82ad04912..d8072476c52 100644 --- a/mps/manual/html/_sources/design/object-debug.txt +++ b/mps/manual/html/_sources/design/object-debug.txt @@ -1,6 +1,405 @@ +.. _design-object-debug: + + .. index:: pair: debugging; design -.. _design-object-debug: -.. include:: ../../converted/Object-debug.rst +Debugging features for client objects +===================================== + +.. mps:prefix:: design.mps.object-debug + + +Introduction +------------ + +:mps:tag:`intro` This is the design for all the various debugging features +that MPS clients (and sometimes MPS developers) can use to discover +what is happening to their objects and the memory space. + +:mps:tag:`readership` MPS developers. + + +Overview +-------- + +:mps:tag:`over.fenceposts` In its current state, this document mostly talks +about fenceposts, straying a little into tagging where theses features +have an effect on each other. + +.. note:: + + There exist other documents that list other required features, and + propose interfaces and implementations. These will eventually be + folded into this one. Pekka P. Pirinen, 1998-09-10. + + +Requirements +------------ + +:mps:tag:`req.fencepost` Try to detect overwrites and underwrites of +allocated blocks by adding fenceposts (source req.product.??? VC++, +req.epcore.fun.debug.support). + +:mps:tag:`req.fencepost.size` The fenceposts should be at least 4 bytes on +either side or 8 bytes if on one side only, with an adjustable content +(although VC++ only has 4 bytes with pattern 0xFDFDFDFD, having +unwisely combined the implementation with other debug features). + +:mps:tag:`req.fencepost.check` There should be a function to check all the +fenceposts (source req.epcore.fun.debug.support). + +:mps:tag:`req.free-block` Try to detect attempts to write and read free +blocks. + +:mps:tag:`req.walk` There should be a way to map ("walk") a user function +over all allocated objects (except PS VM objects), possibly only in a +separate debugging variety/mode (source req.epcore.fun.debug.support). + +:mps:tag:`req.tag` There should be a way to store at least a word of user +data (a "tag", borrowing the SW term) with every object in debugging +mode, to be used in memory dumps (source req.product.??? VC++). + +:mps:tag:`req.tag.walk` The walking function (as required by :mps:ref:`.req.walk`) +should have access to this data (source req.epcore.fun.debug.support). + +:mps:tag:`req.dump.aver` It must be possible to perform a memory dump after +an :c:func:`AVER()` has fired. Naturally, if the information required for +the dump has been corrupted, it will fail, as softly as possible +(source @@@@). + +.. note:: + + There are more requirements, especially about memory dumps and + allocation locations. Pekka P. Pirinen, 1998-09-10. + + +Solution ideas +-------------- + +:mps:tag:`note.assumptions` I've tried not to assume anything about the +coincidence of manual/automatic, formatted/unformatted, and +ap/mps_alloc. I think those questions deserve to be decided on their +own merits. instead of being constrained by a debug feature. + +:mps:tag:`fence.content.repeat` The content of a fencepost could be +specified as a byte/word which used repeatedly to fill the fencepost. + +:mps:tag:`fence.content.template` The content could be given as a template +which is of the right size and is simply copied onto the fencepost. + +:mps:tag:`fence.walk` :mps:ref:`.req.fencepost.check` requires the ability to find +all the allocated objects. In formatted pools, this is not a problem. +In unformatted pools, we could use the walker. It's a feasible +strategy to bet that any pool that might have to support fenceposting +will also have a walking requirement. + +:mps:tag:`fence.tag` Fenceposting also needs to keep track which objects +have fenceposts. unless we manage to do them all. It would be easiest +to put this in the tags. + +:mps:tag:`fence.check.object` A function to check the fenceposts on a given +object would be nice. + +:mps:tag:`fence.ap` AP's could support fenceposting transparently by having +a mode where :c:func:`mps_reserve()` always goes out-of-line and fills in the +fenceposts (the pool's :c:func:`BufferFill()` method isn't involved). This +would leave the MPS with more freedom of implementation, especially +when combined with some of the other ideas. We think doing a function +call for every allocation is not too bad for debugging. + +:mps:tag:`fence.outside-ap` We could also let the client insert their own +fenceposts outside the MPS allocation mechanism. Even if fenceposting +were done like this, we'd still want it to be an MPS feature, so we'd +offer sample C macros for adding the size of the fencepost and filling +in the fencepost pattern. Possibly something like this (while we could +still store the parameters in the pool or allocation point, there +seems little point in doing so in this case, and having them as +explicit parameters to the macros allows the client to specify +constants to gain effiency):: + + #define mps_add_fencepost(size, fp_size) + #define mps_fill_fenceposts(obj, size, fp_size, fp_pattern) + +The client would need to supply their own fencepost checking function, +obviously, but again we could offer one that matches the sample +macros. + +:mps:tag:`fence.tail-only` In automatic pools, the presence of a fencepost +at the head of the allocated block results in the object reference +being an internal pointer. This means that the format or the pool +would need to know about fenceposting and convert between references +and pointers. This would slow down the critical path when fenceposting +is used. This can be ameliorated by putting a fencepost at the tail of +the block only: this obviates the internal pointer problem and could +provide almost the same degree of checking (provided the size was +twice as large), especially in copying pools, where there are normally +no gaps between allocated blocks. In addition to the inescapable +effects on allocation and freeing (including copying and reclaim +thereunder), only scanning would have to know about fenceposts. + +:mps:tag:`fence.tail-only.under` Walking over all the objects in the pool +would be necessary to detect underwrites, as one couldn't be sure that +there is a fencepost before any given object (or where it's located +exactly). If the pool were doing the checking, it could be sure: it +would know about alignments and it could put fenceposts in padding +objects (free blocks will have them because they were once allocated) +so there'd be one on either side of any object (except at the head of +a segment, which is not a major problem, and could be fixed by adding +a padding object at the beginning of every segment). This requires +some cleverness to avoid splinters smaller than the fencepost size, +but it can be done. + +:mps:tag:`fence.wrapper` On formatted pools, fenceposting could be +implemented by "wrapping" the client-supplied format at creation time. +The wrapper can handle the conversion from the fenceposted object and +back. This will be invisible to the client and gives the added benefit +that the wrapper can validate fenceposts on every format operation, +should it desire. That is, the pool would see the fenceposts as part +of the client object, but the client would only see its object; the +format wrapper would translate between the two. Note that hiding the +fenceposts from scan methods, which are required to take a contiguous +range of objects, is a bit complicated. + +:mps:tag:`fence.client-format` The MPS would supply such a wrapper, but +clients could also be allowed to write their own fenceposted formats +(provided they coordinate with allocation, see below). This would make +scanning fenceposted segments more efficient. + +:mps:tag:`fence.wrapper.variable` Furthermore, you could create different +classes of fencepost within a pool, because the fencepost itself could +have a variable format. For instance, you might choose to have the +fencepost be minimal (one to two words) for small objects, and more +detailed/complex for large objects (imagining that large objects are +likely vector-ish and subject to overruns). You could get really fancy +and have the fencepost class keyed to the object class (for example, +different allocation points create different classes of fenceposting). + +:mps:tag:`fence.wrapper.alloc` Even with a wrapped format, allocation and +freeing would still have know about the fenceposts. If allocation +points are used, either MPS-side (:mps:ref:`.fence.ap`) or client-side +(:mps:ref:`.fence.outside-ap`) fenceposting could be used, with the obvious +modifications. + +:mps:tag:`fence.wrapper.alloc.format` We could add three format methods, to +adjust the pointer and the size for alloc and free, to put down the +fenceposts during alloc, and to check them; to avoid slowing down all +allocation, this would require some MOPping to make the format class +affect the choice of the alloc and free methods (see +mail.pekka.1998-06-11.18-18). + +:mps:tag:`fence.wrapper.alloc.size` We could just communicate the size of +the fenceposts between the format and the allocation routines, but +then you couldn't use variable fenceposts (.fence.wrapper.variable). + +.. note:: + + All this applies to copying and reclaim in a straight-forward + manner, I think. + +:mps:tag:`fence.pool.wrapper` Pools can be wrapped as well. This could be a +natural way to represent/implement the fenceposting changes to the +Alloc and Free methods. [@@@@alignment] + +:mps:tag:`fence.pool.new-class` We could simply offer a debugging version of +each pool class (e.g., :c:func:`mps_pool_class_mv_debug()`). As we have seen, +debugging features have synergies which make it advantageous to have a +coordinated implementation, so splitting them up would not just +complicate the client interface, it would also be an implementation +problem; we can turn features on or off with pool init parameters. + +:mps:tag:`fence.pool.abstract` We could simply use pool init parameters only +to control all debugging features (optargs would be useful here). +While there migh be subclasses and wrappers internally, the client +would only see a single pool class; in the internal view, this would +be an abstract class, and the parameters would determine which +concrete class actually gets instantiated. + +:mps:tag:`tag.out-of-line` It would be nice if tags were stored out-of-line, +so they can be used to study allocation patterns and fragmentation +behaviours. Such an implementation of tagging could also easily be +shared among several pools. + + +Architecture +------------ + +:mps:tag:`pool` The implementation is at the pool level, because pools +manage allocated objects. A lot of the code will be generic, +naturally, but the data structures and the control interfaces attach +to pools. In particular, clients will be able to use tagging and +fenceposting separately on each pool. + +:mps:tag:`fence.size` Having fenceposts of adjustable size and pattern is +quite useful. We feel that restricting the size to an integral +multiple of the [pool or format?] alignment is harmless and simplifies +the implementation enormously. + +:mps:tag:`fence.template` We use templates (:mps:ref:`.fence.content.template`) to +fill in the fenceposts, but we do not give any guarantees about the +location of the fenceposts, only that they're properly aligned. This +leaves us the opportunity to do tail-only fenceposting, if we choose. + +:mps:tag:`fence.slop` [see impl.c.dbgpool.FenceAlloc @@@@] + +:mps:tag:`fence.check.free` We check the fenceposts when freeing an object. + +:mps:tag:`unified-walk` Combine the walking and tagging requirements +(:mps:ref:`.req.tag.walk` and @@@@) into a generic facility for walking and +tagging objects with just one interface and one name: tagging. Also +combine the existing formatted object walker into this metaphor, but +allowing the format and tag parameters of the step function be +optional. + +.. note:: + + This part has not been implemented yet Pekka P. Pirinen, + 1998-09-10. + +:mps:tag:`init` It simplifies the implementation of both tagging and +fenceposting if they are always on, so that we don't have to keep +track of which objects have been fenceposted and which have not, and +don't have to have three kinds of tags: for user data, for +fenceposting, and for both. So we determine this at pool init time +(and let fenceposting turn on tagging, if necessary). + +:mps:tag:`pool-parameters` Fencepost templates and tag formats are passed in +as pool parameters. + +:mps:tag:`modularity` While a combined generic implementation of tags and +fenceposts is provided, it is structured so that each part of it could +be implemented by a pool-specific mechanism with a minimum of new +protocol. + +.. note:: + + This will be improved, when we figure out formatted pools -- they + don't need tags for fenceposting. + +:mps:tag:`out-of-space` If there's no room for tags, we will not dip into +the reservoir, just fail to allocate the tag. If the alloc call had a +reservoir permit, we let it succeed even without a tag, and just make +sure the free method will not complain if it can't find a tag. If the +call didn't have a reservoir permit, we free the block allocated for +the object and fail the allocation, so that the client gets a chance +to do whatever low-memory actions they might want to do. + +.. note:: + + Should this depend on whether there is anything in the reservoir? + +This breaks the one-to-one relationship between tags and objects, so +some checks cannot be made, but we do count the "lost" tags. + +.. note:: + + Need to hash out how to do fenceposting in formatted pools. + + +Client interface +---------------- + +:mps:tag:`interface.fenceposting.check` +:c:func:`mps_pool_check_fenceposts()` is a function to check all +fenceposts in a pool (:c:func:`AVER()` if a problem is found) + +.. note:: + + From here on, these are tentative and incomplete. + +.. c:function:: mps_res_t mps_fmt_fencepost_wrap(mps_fmt_t *format_return, mps_arena_t arena, mps_fmt_t format, ...) + +:mps:tag:`interface.fenceposting.format` A function to wrap a format +(class) to provide fenceposting. + +.. c:function:: void (*mps_fmt_adjust_fencepost_t)(size_t *size_io) + +:mps:tag:`interface.fenceposting.adjust` A format method to adjust size of a +block about to be allocted to allow for fenceposts. + +.. c:function:: void (*mps_fmt_put_fencepost_t)(mps_addr_t * addr_io, size_t size) + +:mps:tag:`interface.fenceposting.add` A format method to add a fencepost +around a block about to be allocated. The :c:macro:`NULL` method adds a tail +fencepost. + +.. c:function:: mps_bool_t (*mps_fmt_check_fenceposts_t)(mps_addr_t) + +:mps:tag:`interface.fenceposting.checker` A format method to check the +fenceposts around an object. The :c:macro:`NULL` method checks tails. + +.. c:function:: mps_class_t mps_debug_class(mps_class_t class) + +:mps:tag:`interface.fenceposting.pool` A function to wrap a pool class +to provide fenceposting (note absence of arena parameter). + +``mps_res_t mps_alloc(mps_addr_t *, mps_pool_t, size_t);`` +``mps_res_t mps_alloc_dbg(mps_addr_t *, mps_pool_t, size_t, ...);`` +``mps_res_t mps_alloc_dbg_v(mps_addr_t *, mps_pool_t, size_t, va_list);`` + +:mps:tag:`interface.tags.alloc` Three functions to replace existing +:c:func:`mps_alloc()` (request.???.??? proposes to remove the varargs) + +.. c:function:: void (*mps_objects_step_t)(mps_addr_t addr, size_t size, mps_fmt_t format, mps_pool_t pool, void *tag_data, void *p) +.. c:function:: void mps_pool_walk(mps_arena_t arena, mps_pool_t pool, mps_objects_step_t step, void *p) +.. c:function:: void mps_arena_walk(mps_arena_t arena, mps_objects_step_t step, void *p) + +:mps:tag:`interface.tags.walker` Functions to walk all the allocated +objects in a pool or an arena (only client pools in this case), +``format`` and ``tag_data`` can be :c:macro:`NULL` (``tag_data`` really wants +to be ``void *``, not :c:type:`mps_addr_t`, because it's stored +together with the internal tag data in an MPS internal pool) + + +Examples +-------- + +:mps:tag:`example.debug-alloc` :: + + #define MPS_ALLOC_DBG(res_io, addr_io, pool, size) + MPS_BEGIN + static mps_tag_A_s _ts = { __FILE__, __LINE__ }; + + *res_io = mps_alloc(addr_io, pool, size, _ts_) + MPS_END + + +Implementation +-------------- + +:mps:tag:`new-pool` The client interface to control fenceposting +consists of the new classes :c:func:`mps_pool_class_mv_debug()`, +:c:func:`mps_pool_class_epdl_debug()`, and +:c:func:`mps_pool_class_epdr_debug()`, and their new init parameter of +type :c:type:`mps_pool_debug_option_s`. + +.. note:: + + This is a temporary solution, to get it out without writing lots + of new interface. Pekka P. Pirinen, 1998-09-10. + +:mps:tag:`new-pool.impl` The debug pools are implemented using the "class +wrapper" :c:func:`EnsureDebugClass()`, which produces a subclass with +modified ``init``, ``finish``, ``alloc``, and ``free`` methods. These +methods are implemented in the generic debug class code +(``impl.c.dbgpool``), and are basically wrappers around the superclass +methods (invoked through the ``pool->class->super`` field). To find +the data stored in the class for the debugging features, they use the +``debugMixin`` method provided by the subclass. So to make a debug +subclass, three things should be provided: a structure definition of +the instance containing a :c:type:`PoolDebugMixinStruct`, a pool class +function that uses :c:func:`EnsureDebugClass()`, and a ``debugMixin`` method +that locates the :c:type:`PoolDebugMixinStruct` within an instance. + +:mps:tag:`tags.splay` The tags are stored in a splay tree of tags +allocated from a subsidiary MFS pool. The client needs to specify the +(maximum) size of the client data in a tag, so that the pool can be +created. + +.. note:: + + Lots more should be said, eventually. Pekka P. Pirinen, + 1998-09-10. + + diff --git a/mps/manual/html/_sources/design/old.txt b/mps/manual/html/_sources/design/old.txt index f6999e47531..29525beab6d 100644 --- a/mps/manual/html/_sources/design/old.txt +++ b/mps/manual/html/_sources/design/old.txt @@ -14,32 +14,55 @@ Old design .. toctree:: :numbered: + alloc-frame arena + arenavm bt - cbs + buffer check class-interface collection + diag finalize fix + interface-c + io + lib lock + locus message + message-gc object-debug pool poolamc + poolams + poolawl + poollo + poolmfs + poolmrg poolmvff prot + protan + protli + protsu protocol + pthreadext reservoir root scan seg + shield splay + sso1al telemetry + thread-manager thread-safety trace type version-library version vm + vman + vmo1 + vmso writef diff --git a/mps/manual/html/_sources/design/pool.txt b/mps/manual/html/_sources/design/pool.txt index 992eac425ff..fbfb9fcd36c 100644 --- a/mps/manual/html/_sources/design/pool.txt +++ b/mps/manual/html/_sources/design/pool.txt @@ -1,6 +1,63 @@ +.. _design-pool: + + .. index:: pair: pool class mechanism; design -.. _design-pool: -.. include:: ../../converted/pool.rst +Pool and pool class mechanisms +============================== + +.. mps:prefix:: design.mps.pool + + +Definitions +----------- + +:mps:tag:`def.outer-structure` The "outer structure" (of a pool) is a C +object of type :c:type:`PoolXXXStruct` or the type ``struct PoolXXXStruct`` +itself. + +:mps:tag:`def.generic-structure` The "generic structure" is a C object of +type :c:type:`PoolStruct` (found embedded in the outer-structure) or the +type ``struct PoolStruct`` itself. + + +Defaults +-------- + +:mps:tag:`align` When initialised, the pool gets the default alignment +(:c:macro:`ARCH_ALIGN`). + +:mps:tag:`no` If a pool class doesn't implement a method, and doesn't expect +it to be called, it should use a non-method (``PoolNo*``) which will +cause an assertion failure if they are reached. + +:mps:tag:`triv` If a pool class supports a protocol but does not require any +more than a trivial implementation, it should use a trivial method +(``PoolTriv*``) which will do the trivial thing. + +:mps:tag:`outer-structure.sig` It is good practice to put the signature for +the outer structure at the end (of the structure). This is because +there's already one at the beginning (in the poolStruct) so putting it +at the end gives some extra fencepost checking. + + +Requirements +------------ + +.. note:: + + Placeholder: must derive the requirements from the architecture. + +:mps:tag:`req.fix` :c:func:`PoolFix()` must be fast. + + +Other +----- + +Interface in mpm.h +Types in mpmst.h +See also design.mps.poolclass + + diff --git a/mps/manual/html/_sources/design/poolamc.txt b/mps/manual/html/_sources/design/poolamc.txt index 6bd304ff52e..8ececc616a4 100644 --- a/mps/manual/html/_sources/design/poolamc.txt +++ b/mps/manual/html/_sources/design/poolamc.txt @@ -1,7 +1,798 @@ -.. index:: - pair: AMC pool; design - single: pool; AMC design - .. _design-poolamc: -.. include:: ../../converted/poolamc.rst + +.. index:: + pair: AMC pool class; design + single: pool class; AMC design + + +AMC pool class +============== + +.. mps:prefix:: design.mps.poolamc + pair: AMC pool class; design + single: pool class; AMC design + + +Introduction +~~~~~~~~~~~~ + +:mps:tag:`intro` This document contains a guide (:mps:ref:`.guide`) to the MPS AMC +pool class, followed by the historical initial design +(:mps:ref:`.initial-design`). + +:mps:tag:`readership` Any MPS developer. + + +Guide +~~~~~ + +:mps:tag:`guide` The AMC pool class is a general-purpose automatic +(collecting) pool class. It is intended for most client objects. AMC +is "Automatic, Mostly Copying": it preserves objects by copying, +except when an ambiguous reference 'nails' the object in place. It is +generational. Chain: specify capacity and mortality of generations 0 +to *N* − 1. Survivors from generation *N* − 1 get promoted into an +arena-wide "top" generation (often anachronistically called the +"dynamic" generation, which was the term on the Lisp Machine). + + +Segment states +-------------- + +:mps:tag:`seg.state` AMC segments are in one of three states: "mobile", +"boarded", or "stuck". + +:mps:tag:`seg.state.mobile` Segments are normally **mobile**: all objects on +the seg are un-nailed, and thus may be preserved by copying. + +:mps:tag:`seg.state.boarded` An ambiguous reference to any address within an +segment makes that segment **boarded**: a nailboard is allocated to +record ambiguous references ("nails"), but un-nailed objects on the +segment are still preserved by copying. + +:mps:tag:`seg.state.stuck` Stuck segments only occur in emergency tracing: a +discovery fix to an object in a mobile segment is recorded in the only +non-allocating way available: by making the entire segment **stuck**. + + +Pads +---- + +(See job001809_ and job001811_, and mps/branch/2009-03-31/padding.) + +.. _job001809: http://www.ravenbrook.com/project/mps/issue/job001809/ +.. _job001811: http://www.ravenbrook.com/project/mps/issue/job001811/ + +:mps:tag:`pad` A pad is logically a trivial client object. Pads are created +by the MPS asking the client's format code to create them, to fill up +a space in a segment. Thereafter, the pad appears to the MPS as a +normal client object (that is: the MPS cannot distinguish a pad from a +client object). + +:mps:tag:`pad.reason` AMC creates pads for three reasons: buffer empty +fragment (BEF), large segment padding (LSP), and non-mobile reclaim +(NMR). (Large segment pads were new with job001811_.) + +:mps:tag:`pad.reason.bef` Buffer empty fragment (BEF) pads are made by +:c:func:`AMCBufferEmpty()` whenever it detaches a non-empty buffer from an +AMC segment. Buffer detachment is most often caused because the buffer +is too small for the current buffer reserve request (which may be +either a client requested or a forwarding allocation). Detachment may +happen for other reasons, such as trace flip. + +:mps:tag:`pad.reason.lsp` Large segment padding (LSP) pads are made by +:c:func:`AMCBufferFill()` when the requested fill size is "large" (see `The +LSP payoff calculation`_ below). :c:func:`AMCBufferFill()` fills the buffer +to exactly the size requested by the current buffer reserve operation; +that is: it does not round up to the whole segment size. This prevents +subsequent small objects being placed in the same segment as a single +very large object. If the buffer fill size is less than the segment +size, :c:func:`AMCBufferFill()` fills any remainder with an large segment +pad. + +:mps:tag:`pad.reason.nmr` Non-mobile reclaim (NMR) pads are made by +:c:func:`amcReclaimNailed()`, when performing reclaim on a non-mobile (that +is, either boarded or stuck) segment: + +The more common NMR scenario is reclaim of a boarded segment after a +non-emergency trace. Ambiguous references into the segment are +recorded as nails. Subsequent exact references to a nailed object do +nothing further, but exact refs that do not match a nail cause +preserve-by-copy and leave a forwarding object. Unreachable objects +are not touched during the scan+fix part of the trace. On reclaim, +only nailed objects need to be preserved; others (namely forwarding +pointers and unreachable objects) are replaced by an NMR pad. (Note +that a BEF or LSP pad appears to be an unreachable object, and is +therefore overwritten by an NMR pad). + +The less common NMR scenario is after emergency tracing. Boarded +segments still occur; they may have nailed objects from ambiguous +references, forwarding objects from pre-emergency exact fixes, nailed +objects from mid-emergency exact fixes, and unpreserved objects; +reclaim is as in the non-emergency case. Stuck segments may have +forwarding objects from pre-emergency exact fixes, objects from +mid-emergency fixes, and unreachable objects -- but the latter two are +not distinguishable because there is no nailboard. On reclaim, all +objects except forwarding pointers are preserved; each forwarding +object is replaced by an NMR pad. + +If :c:func:`amcReclaimNailed()` finds no objects to be preserved then it +calls :c:func:`SegFree()` (new with job001809_). + + +Placement pads are okay +----------------------- + +Placement pads are the BEF and LSP pads created in "to-space" when +placing objects into segments. This wasted space is an expected +space-cost of AMC's naive (but time-efficient) approach to placement +of objects into segments. This is normally not a severe problem. (The +worst case is a client that always requests ``ArenaAlign() + 1`` byte +objects: this has a nearly 100% overhead). + + +Retained pads could be a problem +-------------------------------- + +Retained pads are the NMR pads stuck in "from-space": non-mobile +segments that were condemned but have preserved-in-place objects +cannot be freed by :c:func:`amcReclaimNailed()`. The space around the +preserved objects is filled with NMR pads. + +In the worst case, retained pads could waste an enormous amount of +space! A small (one-byte) object could retain a multi-page segment for +as long as the ambiguous reference persists; that is: indefinitely. +Imagine a 256-page (1 MiB) segment containing a very large object +followed by a handful of small objects. An ambiguous reference to one +of the small objects will unfortunately cause the entire 256-page +segment to be retained, mostly as an NMR pad; this is a massive +overhead of wasted space. + +AMC mitigates this worst-case behaviour, by treating large segments +specially. + + +Small, medium, and large segments +--------------------------------- + +AMC categorises segments as **small** (one page), **medium** +(several pages), or **large** (``AMCLargeSegPAGES`` or more):: + + pages = SegSize(seg) / ArenaAlign(arena); + if(pages == 1) { + /* small */ + } else if(pages < AMCLargeSegPAGES) { + /* medium */ + } else { + /* large */ + } + +``AMCLargeSegPAGES`` is currently 8 -- see `The LSP payoff +calculation`_ below. + +AMC might treat "Large" segments specially, in two ways: + +- :mps:tag:`large.single-reserve` A large segment is only used for a single + (large) buffer reserve request; the remainder of the segment (if + any) is immediately padded with an LSP pad. + +- :mps:tag:`large.lsp-no-retain` Nails to such an LSP pad do not cause + AMCReclaimNailed() to retain the segment. + +:mps:ref:`.large.single-reserve` is implemented. See job001811_. + +:mps:ref:`.large.lsp-no-retain` is **not** currently implemented. + +The point of :mps:ref:`.large.lsp-no-retain` would be to avoid retention of +the (large) segment when there is a spurious ambiguous reference to +the LSP pad at the end of the segment. Such an ambiguous reference +might happen naturally and repeatably if the preceding large object is +an array, the array is accessed by an ambiguous element pointer (for +example, on the stack), and the element pointer ends up pointing just +off the end of the large object (as is normal for sequential element +access in C) and remains with that value for a while. (Such an +ambiguous reference could also occur by chance, for example, by +coincidence with an ``int`` or ``float``, or when the stack grows to +include old unerased values). + +Implementing :mps:ref:`.large.lsp-no-retain` is a little tricky. A pad is +indistinguishable from a client object, so AMC has no direct way to +detect, and safely ignore, the final LSP object in the seg. If AMC +could *guarantee* that the single buffer reserve +(:mps:ref:`.large.single-reserve`) is only used for a single *object*, then +:c:func:`AMCReclaimNailed()` could honour a nail at the start of a large seg +and ignore all others; this would be extremely simple to implement. +But AMC cannot guarantee this, because in the MPS Allocation Point +Protocol the client is permitted to make a large buffer reserve and +then fill it with many small objects. In such a case, AMC must honour +all nails (if the buffer reserve request was an exact multiple of +:c:func:`ArenaAlign()`), or all nails except to the last object (if there +was a remainder filled with an LSP pad). Because an LSP pad cannot be +distinguished from a client object, and the requested allocation size +is not recorded, AMC cannot distinguish these two conditions at +reclaim time. Therefore AMC must record whether or not the last object +in the seg is a pad, in order to ignore nails to it. This could be +done by adding a flag to :c:type:`AMCSegStruct`. (This can be done without +increasing the structure size, by making the ``Bool new`` field +smaller than its current 32 bits.) + + +The LSP payoff calculation +-------------------------- + +The LSP fix for job001811_ treats large segments differently. Without +it, after allocating a very large object (in a new very large +multi-page segment), MPS would happily place subsequent small objects +in any remaining space at the end of the segment. This would risk +pathological fragmentation: if these small objects were systematically +preserved by ambiguous refs, enormous NMR pads would be retained along +with them. + +The payoff calculation is a bit like deciding whether or not to +purchase insurance. For single-page and medium-sized segments, we go +ahead and use the remaining space for subsequent small objects. This +is equivalent to choosing **not** to purchase insurance. If the small +objects were to be preserved by ambiguous refs, the retained NMR pads +would be big, but not massive. We expect such ambiguous refs to be +uncommon, so we choose to live with this slight risk of bad +fragmentation. The benefit is that the remaining space is used. + +For large segments, we decide that the risk of using the remainder is +just too great, and the benefit too small, so we throw it away as an +LSP pad. This is equivalent to purchasing insurance: we choose to pay +a known small cost every time, to avoid risking an occasional +disaster. + +To decide what size of segment counts as "large", we must decide how +much uninsured risk we can tolerate, versus how much insurance cost we +can tolerate. The likelihood of ambiguous references retaining objects +is entirely dependent on client behaviour. However, as a sufficient +"one size fits all" policy, I (RHSK 2009-09-14) have judged that +segments smaller than eight pages long do not need to be treated as +large: the insurance cost to "play safe" would be considerable +(wasting up to one page of remainder per seven pages of allocation), +and the fragmentation overhead risk is not that great (at most eight +times worse than the unavoidable minimum). So ``AMCLargeSegPAGES`` is +defined as 8 in config.h. As long as the assumption that most segments +are not ambiguously referenced remains correct, I expect this policy +will be satisfactory. + +To verify that this threshold is acceptable for a given client, +poolamc.c calculates metrics; see `Feedback about retained pages`_ +below. If this one-size-fits-all approach is not satisfactory, +``AMCLargeSegPAGES`` could be made a client-tunable parameter. + + +Retained pages +-------------- + +The reasons why a segment and its pages might be retained are: + +1. ambiguous reference to first-obj: unavoidable page retention (only + the mutator can reduce this, if they so wish, by nulling out ambig + referencess); +2. ambiguous reference to rest-obj: tuning MPS LSP policy could + mitigate this, reducing the likelihood of rest-objs being + co-located with large first-objs; +3. ambiguous reference to final pad: implementing + :mps:ref:`.large.lsp-no-retain` could mitigate this; +4. ambiguous reference to other (NMR) pad: hard to mitigate, as pads + are indistinguishable from client objects; +5. emergency trace; +6. non-object-aligned ambiguous ref: fixed by job001809_; +7. other reason (for example, buffered at flip): not expected to be a + problem. + +This list puts the reasons that are more "obvious" to the client +programmer first, and the more obscure reasons last. + + +Feedback about retained pages +----------------------------- + +(New with job001811_). AMC now accumulates counts of pages condemned +and retained during a trace, in categories according to size and +reason for retention, and emits diagnostic at trace-end via the +``pool->class->traceEnd`` method. See comments on the +:c:type:`PageRetStruct` in poolamc.c. These page-based metrics are not as +precise as actually counting the size of objects, but they require +much less intrusive code to implement, and should be sufficient to +assess whether AMC's page retention policies and behaviour are +acceptable. + + +Initial design +~~~~~~~~~~~~~~ + + +Introduction +------------ + +:mps:tag:`intro` This is the design of the AMC Pool Class. AMC stands for +Automatic Mostly-Copying. This design is highly fragmentory and some +may even be sufficiently old to be misleading. + +:mps:tag:`readership` The intended readership is any MPS developer. + + +Overview +-------- + +:mps:tag:`overview` This class is intended to be the main pool class used by +Harlequin Dylan. It provides garbage collection of objects (hence +"automatic"). It uses generational copying algorithms, but with some +facility for handling small numbers of ambiguous references. Ambiguous +references prevent the pool from copying objects (hence "mostly +copying"). It provides incremental collection. + +.. note:: + + A lot of this design is awesomely old. David Jones, 1998-02-04. + + +Definitions +----------- + +:mps:tag:`def.grain` Grain. An quantity of memory which is both aligned to +the pool's alignment and equal to the pool's alignment in size. That +is, the smallest amount of memory worth talking about. + + +Segments +-------- + +:mps:tag:`seg.class` AMC allocates segments of class :c:type:`AMCSegClass`, which +is a subclass of :c:type:`GCSegClass`. Instances contain a ``segTypeP`` +field, which is of type ``int*``. + +:mps:tag:`seg.gen` AMC organizes the segments it manages into generations. + +:mps:tag:`seg.gen.map` Every segment is in exactly one generation. + +:mps:tag:`seg.gen.ind` The segment's ``segTypeP`` field indicates which +generation (that the segment is in) (an :c:type:`AMCGenStruct` see blah +below). + +:mps:tag:`seg.typep` The ``segTypeP`` field actually points to either the +type field of a generation or to the type field of a nail board. + +:mps:tag:`seg.typep.distinguish` The ``type`` field (which can be accessed +in either case) determines whether the ``segTypeP`` field is pointing +to a generation or to a nail board. + +:mps:tag:`seg.gen.get` The map from segment to generation is implemented by +:c:func:`AMCSegGen()` which deals with all this. + + +Fixing and nailing +------------------ + +.. note:: + + This section contains placeholders for design rather than design + really. David Jones, 1998-02-04. + +:mps:tag:`nailboard` AMC uses a nail board structure for recording ambiguous +references to segments. A nail board is a bit table with one bit per +grain in the segment. + +:mps:tag:`nailboard.create` Nail boards are allocated dynamically whenever a +segment becomes newly ambiguously referenced. + +:mps:tag:`nailboard.destroy` They are deallocated during reclaim. Ambiguous +fixes simply set the appropriate bit in this table. This table is used +by subsequent scans and reclaims in order to work out what objects +were marked. + +:mps:tag:`nailboard.emergency` During emergency tracing two things relating +to nail boards happen that don't normally: + +1. :mps:tag:`nailboard.emergency.nonew` Nail boards aren't allocated when we + have new ambiguous references to segments. + + :mps:tag:`nailboard.emergency.nonew.justify` We could try and allocate a + nail board, but we're in emergency mode so short of memory so it's + unlikely to succeed, and there would be additional code for yet + another error path which complicates things. + +2. :mps:tag:`nailboard.emergency.exact` nail boards are used to record exact + references in order to avoid copying the objects. + + :mps:tag:`nailboard.hyper-conservative` Not creating new nail boards + (:mps:ref:`.nailboard.emergency.nonew` above) means that when we have a new + reference to a segment during emergency tracing then we nail the + entire segment and preserve everything in place. + +:mps:tag:`fix.nail.states` Partition the segment states into four sets: + +1. white segment and not nailed (and has no nail board); +2. white segment and nailed and has no nail board; +3. white segment and nailed and has nail board; +4. the rest. + +:mps:tag:`fix.nail.why` A segment is recorded as being nailed when either +there is an ambiguous reference to it, or there is an exact reference +to it and the object couldn't be copied off the segment (because there +wasn't enough memory to allocate the copy). In either of these cases +reclaim cannot simply destroy the segment (usually the segment will +not be destroyed because it will have live objects on it, though see +:mps:ref:`.nailboard.limitations.middle` below). If the segment is nailed then +we might be using a nail board to mark objects on the segment. +However, we cannot guarantee that being nailed implies a nail board, +because we might not be able to allocate the nail board. Hence all +these states actually occur in practice. + +:mps:tag:`fix.nail.distinguish` The nailed bits in the segment descriptor +(:c:type:`SegStruct`) are used to record whether a segment is nailed or not. +The ``segTypeP`` field of the segment either points to (the "type" +field of) an ``AMCGen`` or to an ``AMCNailBoard``, the type field can +be used to determine which of these is the case. (see :mps:ref:`.seg.typep` +above). + +:mps:tag:`nailboard.limitations.single` Just having a single nail board per +segment prevents traces from improving on the findings of each other: +a later trace could find that a nailed object is no longer nailed or +even dead. Until the nail board is discarded, that is. + +:mps:tag:`nailboard.limitations.middle` An ambiguous reference into the +middle of an object will cause the segment to survive, even if there +are no surviving objects on it. + +:mps:tag:`nailboard.limitations.reclaim` :c:func:`AMCReclaimNailed()` could cover +each block of reclaimed objects between two nailed objects with a +single padding object, speeding up further scans. + + +Emergency tracing +----------------- + +:mps:tag:`emergency.fix` :c:func:`AMCFixEmergency()` is at the core of AMC's +emergency tracing policy (unsurprisingly). :c:func:`AMCFixEmergency()` +chooses exactly one of three options: + +1. use the existing nail board structure to record the fix; +2. preserve and nail the segment in its entirety; +3. snapout an exact (or high rank) pointer to a broken heart to the + broken heart's forwarding pointer. + +If the rank of the reference is ``RankAMBIG`` then it either does (1) +or (2) depending on wether there is an existing nail board or not. +Otherwise (the rank is exact or higher) if there is a broken heart it +is used to snapout the pointer. Otherwise it is as for an +``RankAMBIG`` reference: we either do (1) or (2). + +:mps:tag:`emergency.scan` This is basically as before, the only complication +is that when scanning a nailed segment we may need to do multiple +passes, as :c:func:`FixEmergency()` may introduce new marks into the nail +board. + + +Buffers +------- + +:mps:tag:`buffer.class` AMC uses buffer of class :c:type:`AMCBufClass` (a subclass +of SegBufClass). + +:mps:tag:`buffer.gen` Each buffer allocates into exactly one generation. + +:mps:tag:`buffer.field.gen` ``AMCBuf`` buffer contain a gen field which +points to the generation that the buffer allocates into. + +:mps:tag:`buffer.fill.gen` :c:func:`AMCBufferFill()` uses the generation (obtained +from the ``gen`` field) to initialise the segment's ``segTypeP`` field +which is how segments get allocated in that generation. + +:mps:tag:`buffer.condemn` We condemn buffered segments, but not the contents +of the buffers themselves, because we can't reclaim uncommitted +buffers (see design.mps.buffer for details). If the segment has a +forwarding buffer on it, we detach it. + +.. note:: + + Why? Forwarding buffers are detached because they used to cause + objects on the same segment to not get condemned, hence caused + retention of garbage. Now that we condemn the non-buffered portion + of buffered segments this is probably unnecessary. David Jones, + 1998-06-01. + + But it's probably more efficient than keeping the buffer on the + segment, because then the other stuff gets nailed -- Pekka P. + Pirinen, 1998-07-10. + +If the segment has a mutator buffer on it, we nail the buffer. If the +buffer cannot be nailed, we give up condemning, since nailing the +whole segment would make it survive anyway. The scan methods skip over +buffers and fix methods don't do anything to things that have already +been nailed, so the buffer is effectively black. + + +Types +----- + +:mps:tag:`struct` :c:type:`AMCStruct` is the pool class AMC instance structure. + +:mps:tag:`struct.pool` Like other pool class instances, it contains a +:c:type:`PoolStruct` containing the generic pool fields. + +:mps:tag:`struct.format` The ``format`` field points to a :c:type:`Format` +structure describing the object format of objects allocated in the +pool. The field is intialized by :c:func:`AMCInit()` from a parameter, and +thereafter it is not changed until the pool is destroyed. + +.. note:: + + Actually the format field is in the generic :c:type:`PoolStruct` these + days. David Jones, 1998-09-21. + +.. note:: + + There are lots more fields here. + + +Generations +----------- + +:mps:tag:`gen` Generations partition the segments that a pool manages (see +:mps:ref:`.seg.gen.map` above). + +:mps:tag:`gen.collect` Generations are more or less the units of +condemnation in AMC. And also the granularity for forwarding (when +copying objects during a collection): all the objects which are copied +out of a generation use the same forwarding buffer for allocating the +new copies, and a forwarding buffer results in allocation in exactly +one generation. + +:mps:tag:`gen.rep` Generations are represented using an :c:type:`AMCGenStruct` +structure. + +:mps:tag:`gen.create` All the generation are create when the pool is created +(during :c:func:`AMCInitComm()`). + +:mps:tag:`gen.manage.ring` An AMC's generations are kept on a ring attached +to the :c:type:`AMCStruct` (the ``genRing`` field). + +:mps:tag:`gen.manage.array` They are also kept in an array which is +allocated when the pool is created and attached to the :c:type:`AMCStruct` +(the gens field holds the number of generations, the ``gen`` field +points to an array of ``AMCGen``). + +.. note:: + + it seems to me that we could probably get rid of the ring. David + Jones, 1998-09-22. + +:mps:tag:`gen.number` There are ``AMCTopGen + 2`` generations in total. +"normal" generations numbered from 0 to ``AMCTopGen`` inclusive and an +extra "ramp" generation (see :mps:ref:`.gen.ramp` below). + +:mps:tag:`gen.forward` Each generation has an associated forwarding buffer +(stored in the ``forward`` field of ``AMCGen``). This is the buffer +that is used to forward objects out of this generation. When a +generation is created in :c:func:`AMCGenCreate()`, its forwarding buffer has +a null ``p`` field, indicating that the forwarding buffer has no +generation to allocate in. The collector will assert out (in +:c:func:`AMCBufferFill()` where it checks that ``buffer->p`` is an +``AMCGen``) if you try to forward an object out of such a generation. + +:mps:tag:`gen.forward.setup` All the generation's forwarding buffer's are +associated with generations when the pool is created (just after the +generations are created in :c:func:`AMCInitComm()`). + + +Ramps +----- + +:mps:tag:`ramp` Ramps usefully implement the begin/end +:c:func:`mps_alloc_pattern_ramp()` interface. + +:mps:tag:`gen.ramp` To implement ramping (request.dylan.170423), AMC uses a +special "ramping mode", where promotions are redirected. One +generation is designated the "ramp generation" (``amc->rampGen`` in +the code). + +:mps:tag:`gen.ramp.ordinary` Ordinarily, that is whilst not ramping, objects +are promoted into the ramp generation from younger generations and are +promoted out to older generations. The generation that the ramp +generation ordinarily promotes into is designated the "after-ramp +generation" (``amc->afterRampGen``). + +:mps:tag:`gen.ramp.particular` the ramp generation is the second oldest +generation and the after-ramp generation is the oldest generation. + +:mps:tag:`gen.ramp.possible` In alternative designs it might be possible to +make the ramp generation a special generation that is only promoted +into during ramping, however, this is not done. + +:mps:tag:`gen.ramp.ramping` The ramp generation is promoted into itself +during ramping mode; + +:mps:tag:`gen.ramp.after` after this mode ends, the ramp generation is +promoted into the after-ramp generation as usual. + +:mps:tag:`gen.ramp.after.once` Care is taken to +ensure that there is at least one collection where stuff is promoted +from the ramp generation to the after-ramp generation even if ramping +mode is immediately re-entered. + +:mps:tag:`ramp.mode` This behaviour is controlled in a slightly convoluted +manner by a state machine. The rampMode field of the pool forms an +important part of the state of the machine. + +There are five states: OUTSIDE, BEGIN, RAMPING, FINISH, and +COLLECTING. These appear in the code as ``RampOUTSIDE`` and so on. + +:mps:tag:`ramp.state.cycle.usual` The usual progression of states is a +cycle: OUTSIDE → BEGIN → RAMPING → FINISH → COLLECTING → OUTSIDE. + +:mps:tag:`ramp.count` The pool just counts the number of APs that have begun +ramp mode (and not ended). No state changes occur unless this count +goes from 0 to 1 (starting the first ramp) or from 1 to 0 (leaving the +last ramp). In other words, all nested ramps are ignored (see code in +:c:func:`AMCRampBegin()` and :c:func:`AMCRampEnd()`). + +:mps:tag:`ramp.state.invariant.count` In the OUTSIDE state the count must be +zero. In the BEGIN and RAMPING states the count must be greater than +zero. In the FINISH and COLLECTING states the count is not +constrained. + +:mps:tag:`ramp.state.invariant.forward` When in OUTSIDE, BEGIN, or +COLLECTING, the ramp generation forwards to the after-ramp generation. +When in RAMPING or FINISH, the ramp generation forwards to itself. + +:mps:tag:`ramp.outside` The pool is initially in the OUTSIDE state. The only +transition away from the OUTSIDE state is to the BEGIN state, when a +ramp is entered. + +:mps:tag:`ramp.begin` When the count goes up from zero, the state moves from +COLLECTING or OUTSIDE to BEGIN. + +:mps:tag:`ramp.begin.leave` We can leave the BEGIN state to either the +OUTSIDE or the RAMPING state. + +:mps:tag:`ramp.begin.leave.outside` We go to OUTSIDE if the count drops to 0 +before a collection starts. This shortcuts the usual cycle of states +for small enough ramps. + +:mps:tag:`ramp.begin.leave.ramping` We enter the RAMPING state if a +collection starts that condemns the ramp generation (pedantically when +a new GC begins, and a segment in the ramp generation is condemned, we +leave the BEGIN state, see AMCWhiten). At this point we switch the +ramp generation to forward to itself (:mps:ref:`.gen.ramp.ramping`). + +:mps:tag:`ramp.ramping.leave` We leave the RAMPING state and go to the +FINISH state when the ramp count goes back to zero. Thus, the FINISH +state indicates that we have started collecting the ramp generation +while inside a ramp which we have subsequently finished. + +:mps:tag:`ramp.finish.remain` We remain in the FINISH state until we next +start to collect the ramp generation (condemn it), regardless of +entering or leaving any ramps. This ensures that the ramp generation +will be collected to the after-ramp generation at least once. + +:mps:tag:`ramp.finish.leave` When we next condemn the ramp genearation, we +move to the COLLECTING state. At this point the forwarding generations +are switched back so that the ramp generation promotes into the +after-ramp generation on this collection. + +:mps:tag:`ramp.collecting.leave` We leave the COLLECTING state when the GC +enters reclaim (specifically, when a segment in the ramp generation is +reclaimed), or when we begin another ramp. Ordinarily we enter the +OUTSIDE state, but if the client has started a ramp then we go +directly to the BEGIN state. + +_`.ramp.collect-all` There used to be two flavours of ramps: the +normal one and the collect-all flavour that triggered a full GC after +the ramp end. This was a hack for producing certain Dylan statistics, +and no longer has any effect (the flag is passed to +:c:func:`AMCRampBegin()`, but ignored there). + + +Headers +------- + +:mps:tag:`header` AMC supports a fixed-size header on objects, with the +client pointers pointing after the header, rather than the base of the +memory block. See format documentation for details of the interface. + +:mps:tag:`header.client` The code mostly deals in client pointers, only +computing the base and limit of a block when these are needed (such as +when an object is copied). In several places, the code gets a block of +some sort, a segment or a buffer, and creates a client pointer by +adding the header length (``pool->format->headerLength``). + +:mps:tag:`header.fix` There are two versions of the fix method, due to its +criticality, with (:c:func:`AMCHeaderFix()`) and without (:c:func:`AMCFix()`) +headers. The correct one is selected in :c:func:`AMCInitComm()`, and placed +in the pool's fix field. This is the main reason why fix methods +dispatch through the instance, rather than the class like all other +methods. + + +Old and aging notes below here +------------------------------ + +.. c:function:: void AMCFinish(Pool pool) + +:mps:tag:`finish.forward` If the pool is being destroyed it is OK to destroy +the forwarding buffers, as the condemned set is about to disappear. + + +.. c:function:: void AMCBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) + +:mps:tag:`flush` Removes the connexion between a buffer and a group, so that +the group is no longer buffered, and the buffer is reset and will +cause a refill when next used. + +:mps:tag:`flush.pad` The group is padded out with a dummy object so that it +appears full. + +:mps:tag:`flush.expose` The buffer needs exposing before writing the padding +object onto it. If the buffer is being used for forwarding it might +already be exposed, in this case the segment attached to it must be +covered when it leaves the buffer. See :mps:ref:`.fill.expose`. + +:mps:tag:`flush.cover` The buffer needs covering whether it was being used +for forwarding or not. See :mps:ref:`.flush.expose`. + + +.. c:function:: Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, Pool pool, Buffer buffer, Size size, Bool withReservoirPermit) + +:mps:tag:`fill` Reserve was called on an allocation buffer which was reset, +or there wasn't enough room left in the buffer. Allocate a group for +the new object and attach it to the buffer. + +:mps:tag:`fill.expose` If the buffer is being used for forwarding it may be +exposed, in which case the group attached to it should be exposed. See +:mps:ref:`.flush.cover`. + + +.. c:function:: Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) + +:mps:tag:`fix` Fix a reference to the pool. + +Ambiguous references lock down an entire segment by removing it +from old-space and also marking it grey for future scanning. + +Exact, final, and weak references are merged because the action for an +already forwarded object is the same in each case. After that +situation is checked for, the code diverges. + +Weak references are either snapped out or replaced with +``ss->weakSplat`` as appropriate. + +Exact and final references cause the referenced object to be copied to +new-space and the old copy to be forwarded (broken-heart installed) so +that future references are fixed up to point at the new copy. + +:mps:tag:`fix.exact.expose` In order to allocate the new copy the forwarding +buffer must be exposed. This might be done more efficiently outside +the entire scan, since it's likely to happen a lot. + +:mps:tag:`fix.exact.grey` The new copy must be at least as grey as the old +as it may have been grey for some other collection. + + +.. c:function:: Res AMCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) + +:mps:tag:`scan` Searches for a group which is grey for the trace and scans +it. If there aren't any, it sets the finished flag to true. + + +.. c:function:: void AMCReclaim(Pool pool, Trace trace, Seg seg) + +:mps:tag:`reclaim` After a trace, destroy any groups which are still +condemned for the trace, because they must be dead. + +:mps:tag:`reclaim.grey` Note that this might delete things which are grey +for other collections. This is OK, because we have conclusively proved +that they are dead -- the other collection must have assumed they were +alive. There might be a problem with the accounting of grey groups, +however. + +:mps:tag:`reclaim.buf` If a condemned group still has a buffer attached, we +can't destroy it, even though we know that there are no live objects +there. Even the object the mutator is allocating is dead, because the +buffer is tripped. + + diff --git a/mps/manual/html/_sources/design/poolams.txt b/mps/manual/html/_sources/design/poolams.txt new file mode 100644 index 00000000000..7bd64320d64 --- /dev/null +++ b/mps/manual/html/_sources/design/poolams.txt @@ -0,0 +1,465 @@ +.. _design-poolams: + + +.. index:: + pair: AMS pool class; design + single: pool class; AMS design + + +AMS pool class +============== + +.. mps:prefix:: design.mps.poolams + pair: AMS pool class; design + single: pool class; AMS design + + +Introduction +------------ + +:mps:tag:`intro` This is the design of the AMS pool class. + +:mps:tag:`readership` MM developers. + +:mps:tag:`source` design.mps.buffer, design.mps.trace, design.mps.scan, +design.mps.action and design.mps.class-interface [none of these were +actually used -- pekka 1998-04-21]. No requirements doc [we need a +req.mps that captures the commonalities between the products -- pekka +1998-01-27]. + + +Overview +-------- + +:mps:tag:`overview` This document describes the design of the AMS (Automatic +Mark-and-Sweep) pool class. The AMS pool is a proof-of-concept design +for a mark-sweep pool in the MPS. It's not meant to be efficient, but +it could serve as a model for an implementation of a more advanced +pool (such as EPVM). + + +Requirements +------------ + +:mps:tag:`req.mark-sweep` The pool must use a mark-and-sweep GC algorithm. + +:mps:tag:`req.colour` The colour representation should be as efficient as +possible. + +:mps:tag:`req.incremental` The pool must support incremental GC. + +:mps:tag:`req.ambiguous` The pool must support ambiguous references to +objects in it (but ambiguous references into the middle of an object +do not preserve the object). + +:mps:tag:`req.format` The pool must be formatted, for generality. + +:mps:tag:`req.correct` The design and the implementation should be simple +enough to be seen to be correct. + +:mps:tag:`req.simple` Features not related to mark-and-sweep GC should +initially be implemented as simply as possible, in order to save +development effort. + +:mps:tag:`not-req.grey` We haven't figured out how buffers ought to work +with a grey mutator, so we use :mps:ref:`.req.correct` to allow us to design a +pool that doesn't work in that phase. This is acceptable as long as we +haven't actually implemented grey mutator collection. + + +Architecture +------------ + +Subclassing +........... + +:mps:tag:`subclass` Since we expect to have many mark-and-sweep pools, we +build in some protocol for subclasses to modify various aspects of the +behaviour. Notably there's a subclassable segment class, and a +protocol for performing iteration. + + +Allocation +.......... + +:mps:tag:`align` We divide the segments in grains, each the size of the +format alignment. :mps:tag:`alloc-bit-table` We keep track of allocated +grains using a bit table. This allows a simple implementation of +allocation and freeing using the bit table operators, satisfying +:mps:ref:`.req.simple`, and can simplify the GC routines. Eventually, this +should use some sophisticated allocation technique suitable for +non-moving automatic pools. + +:mps:tag:`buffer` We use buffered allocation, satisfying +:mps:ref:`.req.incremental`. The AMC buffer technique is reused, although it +is not suitable for non-moving pools, but req.simple allows us to do +that for now. + +:mps:tag:`extend` If there's no space in any existing segment, a new segment +is allocated. The actual class is allowed to decide the size of the +new segment. + +:mps:tag:`no-alloc` Do not support :c:func:`PoolAlloc()`, because we can't support +one-phase allocation for a scannable pool (unless we disallow +incremental collection). For exact details, see design.mps.buffer. + +:mps:tag:`no-free` Do not support :c:func:`PoolFree()`, because automatic pools +don't need explicit free and having it encourages clients to use it +(and therefore to have dangling pointers, double frees, and other +memory management errors.) + + +Colours +....... + +:mps:tag:`colour` Objects in a segment which is *not* condemned (for some +trace) take their colour (for this trace) from the segment. + +:mps:tag:`colour.object` Since we need to implement a non-copying GC, we +keep track of the colour of each object in a condemned segment +separately. For this, we use bit tables with a bit for each grain. +This format is fast to access, has better locality than mark bits in +the objects themselves, and allows cheap interoperation with the +allocation bit table. + +:mps:tag:`colour.encoding` As to the details, we follow +analysis.non-moving-colour(3), implementing both the alloc-white +sharing option described in +analysis.non-moving-colour.constraint.reclaim.white-free-bit and the +vanilla three-table option, because the former cannot work with +interior pointers. However, the colour encoding in both is the same. + +:mps:tag:`ambiguous.middle` We will allow ambiguous references into the +middle of an object (as required by :mps:ref:`.req.ambiguous`), using the +trick in analysis.non-moving-colour.interior.ambiguous-only to speed +up scanning. + +:mps:tag:`interior-pointer` Note that non-ambiguous interior pointers are +outlawed. + +:mps:tag:`colour.alloc` Objects are allocated black. This is the most +efficient alternative for traces in the black mutator phase, and +.not-req.grey means that's sufficient. + +.. note:: + + Some day, we need to think about allocating grey or white during + the grey mutator phase. + + +Scanning +........ + +:mps:tag:`scan.segment` The tracer protocol requires (for segment barrier +hits) that there is a method for scanning a segment and turning all +grey objects on it black. This cannot be achieved with a single +sequential sweep over the segment, since objects that the sweep has +already passed may become grey as later objects are scanned. + +:mps:tag:`scan.graph` For a non-moving GC, it is more efficient to trace +along the reference graph than segment by segment. It also allows +passing type information from fix to scan. Currently, the tracer +doesn't offer this option when it's polling for work. + +:mps:tag:`scan.stack` Tracing along the reference graph cannot be done by +recursive descent, because we can't guarantee that the stack won't +overflow. We can, however, maintain an explicit stack of things to +trace, and fall back on iterative methods (:mps:ref:`.scan.iter`) when it +overflows and can't be extended. + +:mps:tag:`scan.iter` As discussed in :mps:ref:`.scan.segment`, when scanning a +segment, we need to ensure that there are no grey objects in the +segment when the scan method returns. We can do this by iterating a +sequential scan over the segment until nothing is grey (see +:mps:ref:`.marked.scan` for details). + +:mps:tag:`scan.iter.only` Some iterative method is needed as a fallback for +the more advanced methods, and as this is the simplest way of +implementing the current tracer protocol, we will start by +implementing it as the only scanning method. + +:mps:tag:`scan.buffer` We do not scan between ScanLimit and Limit of a +buffer (see :mps:ref:`.iteration.buffer`), as usual. + +.. note:: + + design.mps.buffer should explain why this works, but doesn't. + Pekka P. Pirinen, 1998-02-11. + +:mps:tag:`fix.to-black` When fixing a reference to a white object, if the +segment does not refer to the white set, the object cannot refer to +the white set, and can therefore be marked as black immediately +(rather than grey). + + +Implementation +-------------- + +Colour +...... + +:mps:tag:`colour.determine` Following the plan in :mps:ref:`.colour`, if +``SegWhite(seg)`` includes the trace, the colour of an object is given +by the bit tables. Otherwise if ``SegGrey(seg)`` includes the trace, +all the objects are grey. Otherwise all the objects are black. + +:mps:tag:`colour.bits` As we only have searches for runs of zero bits, we use +two bit tables, the non-grey and non-white tables, but this is hidden +beneath a layer of macros talking about grey and white in positive +terms. + +:mps:tag:`colour.single` We have only implemented a single set of mark and +scan tables, so we can only condemn a segment for one trace at a time. +This is checked for in condemnation. If we want to do overlapping +white sets, each trace needs its own set of tables. + +:mps:tag:`colour.check` The grey-and-non-white state is illegal, and free +objects must be white as explained in +analysis.non-moving-colour.contraint.reclaim. + + +Iteration +......... + +:mps:tag:`iteration` Scan, reclaim and other operations need to iterate over +all objects in a segment. We abstract this into a single iteration +function, even though we no longer use it for reclaiming and rarely +for scanning. + +:mps:tag:`iteration.buffer` Iteration skips directly from ScanLimit to Limit +of a buffer. This is because this area may contain +partially-initialized and uninitialized data, which cannot be +processed. Since the iteration skips the buffer, callers need to take +the appropriate action, if any, on it. + +.. note:: + + ScanLimit is used for reasons which are not documented in + design.mps.buffer. + + +Scanning Algorithm +.................. + +:mps:tag:`marked` Each segment has a ``marksChanged`` flag, indicating +whether anything in it has been made grey since the last scan +iteration (:mps:ref:`.scan.iter`) started. This flag only concerns the colour +of objects with respect to the trace for which the segment is +condemned, as this is the only trace for which objects in the segment +are being made grey by fixing. Note that this flag doesn't imply that +there are grey objects in the segment, because the grey objects might +have been subsequently scanned and blackened. + +:mps:tag:`marked.fix` The ``marksChanged`` flag is set :c:macro:`TRUE` by +:c:func:`AMSFix()` when an object is made grey. + +:mps:tag:`marked.scan` :c:func:`AMSScan()` must blacken all grey objects on the +segment, so it must iterate over the segment until all grey objects +have been seen. Scanning an object in the segment might grey another +one (:mps:ref:`.marked.fix`), so the scanner iterates until this flag is +:c:macro:`FALSE`, setting it to :c:macro:`FALSE` before each scan. It is safe to +scan the segment even if it contains nothing grey. + +:mps:tag:`marked.scan.fail` If the format scanner returns failure (see +protocol.mps.scanning), we abort the scan in the middle of a segment. +So in this case the marksChanged flag is set back to TRUE, because we +may not have blackened all grey objects. + +.. note:: + + Is that the best reference for the format scanner? + +:mps:tag:`marked.unused` The ``marksChanged`` flag is meaningless unless the +segment is condemned. We make it :c:macro:`FALSE` in these circumstances. + +:mps:tag:`marked.condemn` Condemnation makes all objects in a segment either +black or white, leaving nothing grey, so it doesn't need to set the +``marksChanged`` flag which must already be :c:macro:`FALSE`. + +:mps:tag:`marked.reclaim` When a segment is reclaimed, it can contain +nothing marked as grey, so the ``marksChanged`` flag must already be +:c:macro:`FALSE`. + +:mps:tag:`marked.blacken` When the tracer decides not to scan, but to call +:c:func:`PoolBlacken()`, we know that any greyness can be removed. +:c:func:`AMSBlacken()` does this and resets the ``marksChanged`` flag, if it +finds that the segment has been condemned. + +:mps:tag:`marked.clever` AMS could be clever about not setting the +``marksChanged`` flag, if the fixed object is ahead of the current +scan pointer. It could also keep low- and high-water marks of grey +objects, but we don't need to implement these improvements at first. + + +Allocation +.......... + +:mps:tag:`buffer-init` We take one init arg to set the Rank on the buffer, +just to see how it's done. + +:mps:tag:`no-bit` As an optimization, we won't use the alloc bit table until +the first reclaim on the segment. Before that, we just keep a +high-water mark. + +:mps:tag:`fill` :c:func:`AMSBufferFill()` takes the simplest approach: it iterates +over the segments in the pool, looking for one which can be used to +refill the buffer. + +:mps:tag:`fill.colour` The objects allocated from the new buffer must be +black for all traces (:mps:ref:`.colour.alloc`), so putting it on a black +segment (meaning one where neither ``SegWhite(seg)`` nor +``SegGrey(seg)`` include the trace, see :mps:ref:`.colour.determine`) is +obviously OK. White segments (where ``SegWhite(seg)`` includes the +trace) are also fine, as we can use the colour tables to make it +black. At first glance, it seems we can't put it on a segment that is +grey but not white for some trace (one where ``SegWhite(seg)`` doesn't +include the trace, but ``SegGrey(seg)`` does), because the new objects +would become grey as the buffer's ScanLimit advanced. However, in many +configurations, the mutator would hit a barrier as soon as it started +initializing the object, which would flip the buffer. In fact, the +current (2002-01) implementation of buffers assumes buffers are black, +so they'd better. + +:mps:tag:`fill.colour.reclaim` In fact, putting a buffer on a condemned +segment will screw up the accounting in :c:func:`AMCReclaim()`, so it's +disallowed. + +:mps:tag:`fill.slow` :c:func:`AMSBufferFill()` gets progressively slower as more +segments fill up, as it laboriously checks whether the buffer can be +refilled from each segment, by inspecting the allocation bit map. This +is helped a bit by keeping count of free grains in each segment, but +it still spends a lot of time iterating over all the full segments +checking the free size. Obviously, this can be much improved (we could +keep track of the largest free block in the segment and in the pool, +or we could keep the segments in some more efficient structure, or we +could have a real free list structure). + +:mps:tag:`fill.extend` If there's no space in any existing segment, the +``segSize`` method is called to decide the size of the new segment to +allocate. If that fails, the code tries to allocate a segment that's +just large enough to satisfy the request. + +:mps:tag:`empty` :c:func:`AMSBufferEmpty()` makes the unused space free, since +there's no reason not to. We have to adjust the colour tables as well, +since these grains were black and now they need to be white (or at +least encoded -G and W). + +:mps:tag:`reclaim.empty.buffer` Segments which after reclaim only contain a +buffer could be destroyed by trapping the buffer, but there's no point +to this. + + +Initialization +.............. + +:mps:tag:`init` The initialization method :c:func:`AMSInit()` takes three +additional arguments: the format of objects allocated in the pool, the +chain that controls GC timing, and a flag for supporting ambiguous +references. + +:mps:tag:`init.share` If support for ambiguity is required, the +``shareAllocTable`` flag is reset to indicate the pool uses three +separate bit tables, otherwise it is set and the pool shares a table +for non-white and alloc (see :mps:ref:`.colour.encoding`). + +:mps:tag:`init.align` The pool alignment is set equal to the format +alignment (see design.mps.align). + +:mps:tag:`init.internal` Subclasses call :c:func:`AMSInitInternal()` to avoid the +problems of sharing ``va_list`` and emitting a superfluous +``PoolInitAMS`` event. + + +Condemnation +............ + +:mps:tag:`condemn.buffer` Buffers are not condemned, instead they are +coloured black, to make sure that the objects allocated will be black, +following :mps:ref:`.colour.alloc` (or, if you wish, because buffers are +ignored like free space, so need the same encoding). + + +Reclaim +....... + +:mps:tag:`reclaim` Reclaim uses either of analysis.non-moving-colour +.constraint.reclaim.white-free-bit (just reuse the non-white table as +the alloc table) or +analysis.non-moving-colour.constraint.reclaim.free-bit (copy it), +depending on the shareAllocTable flag (as set by :mps:ref:`.init.share`). +However, bit table still has to be iterated over to count the free +grains. Also, in a debug pool, each white block has to be splatted. + + +Segment merging and splitting +............................. + +:mps:tag:`split-merge` We provide methods for splitting and merging AMS +segments. The pool implementation doesn't cause segments to be split +or merged -- but a subclass might want to do this (see +:mps:ref:`.stress.split-merge`). The methods serve as an example of how to +implement this facility. + +:mps:tag:`split-merge.constrain` There are some additional constraints on +what segments may be split or merged: + +- :mps:tag:`split-merge.constrain.align` Segments may only be split or + merged at an address which is aligned to the pool alignment as well + as to the arena alignment. + + :mps:tag:`split-merge.constrain.align.justify` This constraint is implied + by the design of allocation and colour tables, which cannot + represent segments starting at unaligned addresses. The constraint + only arises if the pool alignment is larger than the arena + alignment. There's no requirement to split segments at unaligned + addresses. + +- :mps:tag:`split-merge.constrain.empty` The higher segment must be empty. + That is, the higher segment passed to :c:func:`SegMerge()` must be empty, + and the higher segment returned by :c:func:`SegSplit()` must be empty. + + :mps:tag:`split-merge.constrain.empty.justify` This constraint makes the + code significantly simpler. There's no requirement for a more + complex solution at the moment (as the purpose is primarily + pedagogic). + +:mps:tag:`split-merge.fail` The split and merge methods are not proper +anti-methods for each other (see +design.mps.seg.split-merge.fail.anti.no). Methods will not reverse the +side-effects of their counterparts if the allocation of the colour and +allocation bit tables should fail. Client methods which over-ride +split and merge should not be written in such a way that they might +detect failure after calling the next method, unless they have reason +to know that the bit table allocations will not fail. + + +Testing +------- + +:mps:tag:`stress` There's a stress test, MMsrc!amsss.c, that does 800 kB of +allocation, enough for about three GCs. It uses a modified Dylan +format, and checks for corruption by the GC. Both ambiguous and exact +roots are tested. + +:mps:tag:`stress.split-merge` There's also a stress test for segment +splitting and merging, MMsrc!segsmss.c. This is similar to amsss.c -- +but it defines a subclass of AMS, and causes segments to be split and +merged. Both buffered and non-buffered segments are split / merged. + + +Notes +----- + +:mps:tag:`addr-index.slow` Translating from an address to and from a grain +index in a segment uses macros such as :c:macro:`AMS_INDEX` and +:c:macro:`AMS_INDEX_ADDR`. These are slow because they call :c:func:`SegBase()` on +every translation -- we could cache that. + +:mps:tag:`grey-mutator` To enforce the restriction set in :mps:ref:`.not-req.grey` +we check that all the traces are flipped in :c:func:`AMSScan()`. It would be +good to check in :c:func:`AMSFix()` as well, but we can't do that, because +it's called during the flip, and we can't tell the difference between +the flip and the grey mutator phases with the current tracer +interface. + + diff --git a/mps/manual/html/_sources/design/poolawl.txt b/mps/manual/html/_sources/design/poolawl.txt new file mode 100644 index 00000000000..fa75673dc27 --- /dev/null +++ b/mps/manual/html/_sources/design/poolawl.txt @@ -0,0 +1,539 @@ +.. _design-poolawl: + + +.. index:: + pair: AWL pool class; design + single: pool class; AWL design + + +AWL pool class +============== + +.. mps:prefix:: design.mps.poolawl + pair: AWL pool class; design + single: pool class; AWL design + + +Introduction +------------ + +:mps:tag:`readership` Any MPS developer. + +:mps:tag:`intro` The AWL (Automatic Weak Linked) pool is used to manage +Dylan Weak Tables (see req.dylan.fun.weak). Currently the design is +specialised for Dylan Weak Tables, but it could be generalised in the +future. + + +Requirements +------------ + +See req.dylan.fun.weak. + +See meeting.dylan.1997-02-27(0) where many of the requirements for +this pool were first sorted out. + +Must satisfy request.dylan.170123. + +:mps:tag:`req.obj-format` Only objects of a certain format need be +supported. This format is a subset of the Dylan Object Format. The +pool uses the first slot in the fixed part of an object to store an +association. See mail.drj.1997-03-11.12-05 + + +Definitions +----------- + +:mps:tag:`def.grain` alignment grain, grain. A grain is a range of addresses +where both the base and the limit of the range are aligned and the +size of range is equal to the (same) alignment. In this context the +alignment is the pool's alignment (``pool->alignment``). The grain is +the unit of allocation, marking, scanning, etc. + + +Overview +-------- + +:mps:tag:`overview` + +:mps:tag:`overview.ms` The pool is mark and sweep. :mps:tag:`overview.ms.justify` +Mark-sweep pools are slightly easier to write (than moving pools), and +there are no requirements (yet) that this pool be high performance or +moving or anything like that. + +:mps:tag:`overview.alloc` It is possible to allocate weak or exact objects +using the normal reserve/commit AP protocol. +:mps:tag:`overview.alloc.justify` Allocation of both weak and exact objects +is required to implement Dylan Weak Tables. Objects are formatted; the +pool uses format A. + +:mps:tag:`overview.scan` The pool handles the scanning of weak objects +specially so that when a weak reference is deleted the corresponding +reference in an associated object is deleted. The associated object is +determined by using information stored in the object itself (see +:mps:ref:`.req.obj-format`). + + +Interface +--------- + +:mps:tag:`if.init` The init method takes one extra parameter in the vararg +list. This parameter should have type :c:type:`Format` and be a format +object that describes the format of the objects to be allocated in +this pool. The format should support scan and skip methods. There is +an additional restriction on the layout of objects, see +:mps:ref:`.req.obj-format`. + +:mps:tag:`if.buffer` The :c:func:`BufferInit()` method takes one extra parameter +in the vararg list. This parameter should be either ``RankEXACT`` or +``RankWEAK``. It determines the rank of the objects allocated using +that buffer. + + +Data structures +--------------- + +:mps:tag:`sig` This signature for this pool will be 0x519bla3l (SIGPooLAWL). + +:mps:tag:`poolstruct` The class specific pool structure is:: + + struct AWLStruct { + PoolStruct poolStruct; + Format format; + Shift alignShift; + ActionStruct actionStruct; + double lastCollected; + Serial gen; + Sig sig; + } + +:mps:tag:`poolstruct.format` The format field is used to refer to the object +format. The object format is passed to the pool during pool creation. + +:mps:tag:`poolstruct.alignshift` The ``alignShift`` field is the +``SizeLog2`` of the pool's alignment. It is computed and initialised +when a pool is created. It is used to compute the number of alignment +grains in a segment which is the number of bits need in the segment's +mark and alloc bit table (see :mps:ref:`.awlseg.bt`, :mps:ref:`.awlseg.mark`, and +:mps:ref:`.awlseg.alloc` below). + +.. note:: + + Clarify this. + +:mps:tag:`poolstruct.actionStruct` Contains an Action which is used to +participate in the collection benefit protocol. See :c:func:`AWLBenefit()` +below for a description of the algorithm used for determining when to +collect. + +:mps:tag:`poolstruct.lastCollected` Records the time (using the mutator +total allocation clock, ie that returned by +:c:func:`ArenaMutatorAllocSize()`) of the most recent call to either +:c:func:`AWLInit()` or :c:func:`AWLTraceBegin()` for this pool. So this is the +time of the beginning of the last collection of this pool. Actually +this isn't true because the pool can be collected without +:c:func:`AWLTraceBegin()` being called (I think) as it will get collected by +being in the same zone as another pool/generation that is being +collected (which it does arrange to be, see the use of the gen field +in :mps:ref:`.poolstruct.gen` below and :mps:ref:`.fun.awlsegcreate.where` below). + +:mps:tag:`poolstruct.gen` This part of the mechanism by which the pool +arranges to be in a particular zone and arranges to be collected +simultaneously with other cohorts in the system. ``gen`` is the +generation that is used in expressing a generation preference when +allocating a segment. The intention is that this pool will get +collected simultaneously with any other segments that are also +allocated using this generation preference (when using the VM arena, +generation preferences get mapped more or less to zones, each +generation to a unique set of zones in the ideal case). Whilst AWL is +not generational it is expected that this mechanism will arrange for +it to be collected simultaneously with some particular generation of +AMC. + +:mps:tag:`poolstruct.gen.1` At the moment the ``gen`` field is set for all +AWL pools to be 1. + +:mps:tag:`awlseg` The pool defines a segment class :c:type:`AWLSegClass`, which is +a subclass of :c:type:`GCSegClass` (see +design.mps.seg.over.hierarchy.gcseg). All segments allocated by the +pool are instances of this class, and are of type ``AWLSeg``, for +which the structure is:: + + struct AWLSegStruct { + GCSegStruct gcSegStruct; + BT mark; + BT scanned; + BT alloc; + Count grains; + Count free; + Count singleAccesses; + AWLStatSegStruct stats; + Sig sig; + } + +:mps:tag:`awlseg.bt` The mark, alloc, and scanned fields are bit-tables (see +design.mps.bt). Each bit in the table corresponds to a a single +alignment grain in the pool. + +:mps:tag:`awlseg.mark` The mark bit table is used to record mark bits during +a trace. :c:func:`AWLCondemn()` (see :mps:ref:`.fun.condemn` below) sets all the +bits of this table to zero. Fix will read and set bits in this table. +Currently there is only one mark bit table. This means that the pool +can only be condemned for one trace. + +:mps:tag:`awlseg.mark.justify` This is simple, and can be improved later +when we want to run more than one trace. + +:mps:tag:`awlseg.scanned` The scanned bit-table is used to note which +objects have been scanned. Scanning (see :mps:ref:`.fun.scan` below) a segment +will find objects that are marked but not scanned, scan each object +found and set the corresponding bits in the scanned table. + +:mps:tag:`awlseg.alloc` The alloc bit table is used to record which portions +of a segment have been allocated. Ranges of bits in this table are set +when a buffer is attached to the segment. When a buffer is flushed (ie +:c:func:`AWLBufferEmpty()` is called) from the segment, the bits +corresponding to the unused portion at the end of the buffer are +reset. + +:mps:tag:`awlseg.alloc.invariant` A bit is set in the alloc table if and +only if the corresponding address is currently being buffered, or the +corresponding address lies within the range of an allocated object. + +:mps:tag:`awlseg.grains` The grains field is the number of grains that fit +in the segment. Strictly speaking this is not necessary as it can be +computed from ``SegSize`` and AWL's alignment, however, precalculating +it and storing it in the segment makes the code simpler by avoiding +lots of repeated calculations. + +:mps:tag:`awlseg.free` A conservative estimate of the number of free grains +in the segment. It is always guaranteed to be greater than or equal to +the number of free grains in the segment, hence can be used during +allocation to quickly pass over a segment. + +.. note:: + + Maintained by blah and blah. Unfinished obviously. + + +Functions +--------- + +.. note:: + + How will pool collect? It needs an action structure. + +External +........ + +.. c:function:: Res AWLInit(Pool pool, va_list arg) + +:mps:tag:`fun.init` :c:type:`AWLStruct` has four fields, each one needs initializing. + +:mps:tag:`fun.init.poolstruct` The ``poolStruct`` field has already been +initialized by generic code (impl.c.pool). + +:mps:tag:`fun.init.format` The format will be copied from the argument list, +checked, and written into this field. + +:mps:tag:`fun.init.alignshift` The ``alignShift`` will be computed from the +pool alignment and written into this field. + +:mps:tag:`fun.init.sig` The ``sig`` field will be initialized with the +signature for this pool. + +.. c:function:: Res AWLFinish(Pool pool) + +:mps:tag:`fun.finish` Iterates over all segments in the pool and destroys +each segment (by calling :c:func:`SegFree()`). Overwrites the sig field in +the :c:type:`AWLStruct`. Finishing the generic pool structure is done by the +generic pool code (impl.c.pool). + +:mps:tag:`fun.alloc` :c:func:`PoolNoAlloc()` will be used, as this class does not +implement alloc. + +:mps:tag:`fun.free` :c:func:`PoolNoFree()` will be used, as this class does not +implement free. + +.. c:function:: Res AWLBufferFill(Seg *segReturn, Addr *baseReturn, Pool pool, Buffer buffer, Size size) + +:mps:tag:`fun.fill` This zips round all the the segments applying +:c:func:`AWLSegAlloc()` to each segment that has the same rank as the +buffer. :c:func:`AWLSegAlloc()` attempts to find a free range, if it finds a +range then it may be bigger than the actual request, in which case the +remainder can be used to "fill" the rest of the buffer. If no free +range can be found in an existing segment then a new segment will be +created (which is at least large enough). The range of buffered +addresses is marked as allocated in the segment's alloc table. + +.. c:function:: void AWLBufferEmpty(Pool pool, Buffer buffer) + +:mps:tag:`fun.empty` Locates the free portion of the buffer, that is the +memory between the init and the limit of the buffer and records these +locations as being free in the relevant alloc table. The segment that +the buffer is pointing at (which contains the alloc table that needs +to be dinked with) is available via :c:func:`BufferSeg()`. + +:mps:tag:`fun.benefit` The benefit returned is the total amount of mutator +allocation minus the ``lastRembemberedSize`` minus 10 MiB, so the pool +becomes an increasingly good candidate for collection at a constant +(mutator allocation) rate, crossing the 0 line when there has been +10 MiB of allocation since the (beginning of the) last collection. So +it gets collected approximately every 10 MiB of allocation. Note that +it will also get collected by virtue of being in the same zone as some +AMC generation (assuming there are instantiated AMC pools), see +:mps:ref:`.poolstruct.gen` above. + +.. c:function:: Res AWLCondemn(Pool pool, Trace trace, Seg seg) + +:mps:tag:`fun.condemn` The current design only permits each segment to be +condemned for one trace (see :mps:ref:`.awlseg.mark`). This function checks +that the segment is not condemned for any trace (``seg->white == +TraceSetEMPTY``). The segment's mark bit-table is reset, and the +whiteness of the seg (``seg->white``) has the current trace added to +it. + +.. c:function:: void AWLGrey(Pool pool, Trace trace, Seg seg) + +:mps:tag:`fun.grey` If the segment is not condemned for this trace the +segment's mark table is set to all 1s and the segment is recorded as +being grey. + +.. c:function:: Res AWLScan(ScanState ss, Pool pool, Seg seg) + +:mps:tag:`fun.scan` + +:mps:tag:`fun.scan.overview` The scanner performs a number of passes over +the segment, scanning each marked and unscanned (grey) object that is +finds. + +:mps:tag:`fun.scan.overview.finish` It keeps perform a pass over the segment +until it is finished. + +:mps:tag:`fun.scan.overview.finish.condition` A condition for finishing is +that no new marks got placed on objects in this segment during the +pass. + +:mps:tag:`fun.scan.overview.finish.approximation` We use an even stronger +condition for finishing that assumes that scanning any object may +introduce marks onto this segment. It is finished when a pass results +in scanning no objects (that is, all objects were either unmarked or +both marked and scanned). + +:mps:tag:`fun.scan.overview.finished-flag` There is a flag called +``finished`` which keeps track of whether we should finish or not. We +only ever finish at the end of a pass. At the beginning of a pass the +flag is set. During a pass if any objects are scanned then the +``finished`` flag is reset. At the end of a pass if the ``finished`` +flag is still set then we are finished. No more passes take place and +the function returns. + +:mps:tag:`fun.scan.pass` A pass consists of a setup phase and a repeated +phase. + +:mps:tag:`fun.scan.pass.buffer` The following assumes that in the general +case the segment is buffered; if the segment is not buffered then the +actions that mention buffers are not taken (they are unimportant if +the segment is not buffered). + +:mps:tag:`fun.scan.pass.p` The pass uses a cursor called ``p`` to progress +over the segment. During a pass ``p`` will increase from the base +address of the segment to the limit address of the segment. When ``p`` +reaches the limit address of the segment, the pass in complete. + +:mps:tag:`fun.scan.pass.setup` ``p`` initially points to the base address of +the segment. + +:mps:tag:`fun.scan.pass.repeat` The following comprises the repeated phase. +The repeated phase is repeated until the pass completion condition is +true (that is, ``p`` has reached the limit of the segment, see +:mps:ref:`.fun.scan.pass.p` above and :mps:ref:`.fun.scan.pass.repeat.complete` +below). + +:mps:tag:`fun.scan.pass.repeat.complete` If ``p`` is equal to the segment's +limit then we are done. We proceed to check whether any further passes +need to be performed (see :mps:ref:`.fun.scan.pass.more` below). + +:mps:tag:`fun.scan.pass.repeat.free` If ``!alloc(p)`` (the grain is free) +then increment ``p`` and return to the beginning of the loop. + +:mps:tag:`fun.scan.pass.repeat.buffer` If ``p`` is equal to the buffer's +ScanLimit, as returned by :c:func:`BufferScanLimit()`, then set ``p`` equal +to the buffer's Limit, as returned by :c:func:`BufferLimit()` and return to +the beginning of the loop. + +:mps:tag:`fun.scan.pass.repeat.object-end` The end of the object is located +using the ``format->skip`` method. + +:mps:tag:`fun.scan.pass.repeat.object` if ``mark(p) && !scanned(p)`` then +the object pointed at is marked but not scanned, which means we must +scan it, otherwise we must skip it. + +:mps:tag:`fun.scan.pass.repeat.object.dependent` To scan the object the +object we first have to determine if the object has a dependent object (see +:mps:ref:`.req.obj-format`). + +:mps:tag:`fun.scan.pass.repeat.object.dependent.expose` If it has a +dependent object then we must expose the segment that the dependent +object is on (only if the dependent object actually points to MPS +managed memory) prior to scanning and cover the segment subsequent to +scanning. + +:mps:tag:`fun.scan.pass.repeat.object.dependent.summary` The summary of the +dependent segment must be set to ``RefSetUNIV`` to reflect the fact +that we are allowing it to be written to (and we don't know what gets +written to the segment). + +:mps:tag:`fun.scan.pass.repeat.object.scan` The object is then scanned by +calling the format's scan method with base and limit set to the +beginning and end of the object (:mps:tag:`fun.scan.scan.improve.single` A +scan1 format method would make it slightly simpler here). Then the +finished flag is cleared and the bit in the segment's scanned table is +set. + +:mps:tag:`fun.scan.pass.repeat.advance` ``p`` is advanced past the object +and we return to the beginning of the loop. + +:mps:tag:`fun.scan.pass.more` At the end of a pass the finished flag is +examined. + +:mps:tag:`fun.scan.pass.more.not` If the finished flag is set then we are +done (see :mps:ref:`.fun.scan.overview.finished-flag` above), :c:func:`AWLScan()` +returns. + +:mps:tag:`fun.scan.pass.more.so` Otherwise (the finished flag is reset) we +perform another pass (see :mps:ref:`.fun.scan.pass` above). + +.. c:function:: Res AWLFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) + +:mps:tag:`fun.fix` ``ss->wasMarked`` is set to :c:macro:`TRUE` (clear compliance +with design.mps.fix.protocol.was-marked.conservative). + +If the rank (``ss->rank``) is ``RankAMBIG`` then fix returns +immediately unless the reference is aligned to the pool alignment. + +If the rank (``ss->rank``) is ``RankAMBIG`` then fix returns +immediately unless the referenced grain is allocated. + +The bit in the marked table corresponding to the referenced grain will +be read. If it is already marked then fix returns. Otherwise (the +grain is unmarked), ``ss->wasMarked`` is set to :c:macro:`FALSE`, the +remaining actions depend on whether the rank (``ss->rank``) is +``RankWEAK`` or not. If the rank is weak then the reference is +adjusted to 0 (see design.mps.weakness) and fix returns. If the rank +is something else then the mark bit corresponding to the referenced +grain is set, and the segment is greyed using :c:func:`TraceSegGreyen()`. + +Fix returns. + + +.. c:function:: void AWLReclaim(Pool pool, Trace trace, Seg seg) + +:mps:tag:`fun.reclaim` This iterates over all allocated objects in the +segment and frees objects that are not marked. When this iteration is +complete the marked array is completely reset. + +``p`` points to base of segment. Then:: + + while(p < SegLimit(seg) { + if(!alloc(p)) { ++p;continue; } + q = skip(p) /* q points to just past the object pointed at by p */ + if !marked(p) free(p, q); /* reset the bits in the alloc table from p to q-1 inclusive. */ + p = q + } + +Finally, reset the entire marked array using :c:func:`BTResRange()`. + +:mps:tag:`fun.reclaim.improve.pad` Consider filling free ranges with padding +objects. Now reclaim doesn't need to check that the objects are +allocated before skipping them. There may be a corresponding change +for scan as well. + +.. c:function:: Res AWLDescribe(Pool pool, mps_lib_FILE *stream) + +:mps:tag:`fun.describe` + + +Internal +........ + +.. c:function:: Res AWLSegCreate(AWLSeg *awlsegReturn, Size size) + +:mps:tag:`fun.awlsegcreate` Creates a segment of class :c:type:`AWLSegClass` of size at least ``size``. + +:mps:tag:`fun.awlsegcreate.size.round` ``size`` is rounded up to an +``ArenaAlign`` before requesting the segment. +:mps:tag:`fun.awlsegcreate.size.round.justify` The arena requires that all +segment sizes are aligned to the ``ArenaAlign``. + +:mps:tag:`fun.awlsegcreate.where` The segment is allocated using a +generation preference, using the generation number stored in the +:c:type:`AWLStruct` (the ``gen`` field), see :mps:ref:`.poolstruct.gen` above. + +.. c:function:: Res awlSegInit(Seg seg, Pool pool, Addr base, Size size, Bool reservoirPermit, va_list args) + +:mps:tag:`fun.awlseginit` Init method for :c:type:`AWLSegClass`, called for +:c:func:`SegAlloc()` whenever an ``AWLSeg`` is created (see +:mps:ref:`.fun.awlsegcreate` above). + +:mps:tag:`fun.awlseginit.tables` The segment's mark scanned and alloc tables +(see :mps:ref:`.awlseg.bt` above) are allocated and initialised. The segment's +grains field is computed and stored. + +.. c:function:: void awlSegFinish(Seg seg) + +:mps:tag:`fun.awlsegfinish` Finish method for :c:type:`AWLSegClass`, called from +:c:func:`SegFree()`. Will free the segment's tables (see :mps:ref:`.awlseg.bt`). + +.. c:function:: Bool AWLSegAlloc(Addr *baseReturn, Addr *limitReturn, AWLSeg awlseg, AWL awl, Size size) + +:mps:tag:`fun.awlsegalloc` Will search for a free block in the segment that +is at least size bytes long. The base address of the block is returned +in ``*baseReturn``, the limit of the entire free block (which must be +at least as large size and may be bigger) is returned in +``*limitReturn``. The requested size is converted to a number of +grains, :c:func:`BTFindResRange()` is called to find a run of this length in +the alloc bit-table (:mps:ref:`.awlseg.alloc`). The return results (if it is +successful) from :c:func:`BTFindResRange()` are in terms of grains, they are +converted back to addresses before returning the relevant values from +this function. + +.. c:function:: Bool AWLDependentObject(Addr *objReturn, Addr parent) + +:mps:tag:`fun.dependent-object` This function abstracts the association +between an object and its linked dependent (see :mps:ref:`.req.obj-format`). +It currently assumes that objects are Dylan Object formatted according +to design.dylan.container (see analysis.mps.poolawl.dependent.abstract +for suggested improvements). An object has a dependent object iff the +second word of the object, that is, ``((Word *)parent)[1]``, is +non-:c:macro:`NULL`. The dependent object is the object referenced by the +second word and must be a valid object. + +This function assumes objects are in Dylan Object Format (see +design.dylan.container). It will check that the first word looks like +a Dylan wrapper pointer. It will check that the wrapper indicates that +the wrapper has a reasonable format (namely at least one fixed field). +If the second word is :c:macro:`NULL` it will return :c:macro:`FALSE`. If the second +word is non-:c:macro:`NULL` then the contents of it will be assigned to +``*objReturn``, and it will return :c:macro:`TRUE`. + + +Test +---- + +- must create Dylan objects. +- must create Dylan vectors with at least one fixed field. +- must allocate weak thingies. +- must allocate exact tables. +- must link tables together. +- must populate tables with junk. +- some junk must die. + +Use an LO pool and an AWL pool. Three buffers. One buffer for the LO +pool, one exact buffer for the AWL pool, one weak buffer for the AWL +pool. + +Initial test will allocate one object from each buffer and then +destroy all buffers and pools and exit + + diff --git a/mps/manual/html/_sources/design/poollo.txt b/mps/manual/html/_sources/design/poollo.txt new file mode 100644 index 00000000000..8a51b2ebb2f --- /dev/null +++ b/mps/manual/html/_sources/design/poollo.txt @@ -0,0 +1,249 @@ +.. _design-poollo: + + +.. index:: + pair: LO pool class; design + single: pool class; LO design + + +LO pool class +============= + +.. mps:prefix:: design.mps.poollo + pair: LO pool class; design + single: pool class; LO design + + +Introduction +------------ + +:mps:tag:`readership` Any MPS developer. + +:mps:tag:`intro` The LO (Leaf Object) pool class is a pool class developed +for DylanWorks. It is designed to manage objects that have no +references (leaf objects) such as strings, bit tables, etc. It is a +garbage collected pool (in that objects allocated in the pool are +automatically reclaimed when they are discovered to be unreachable. + +.. note:: + + Need to sort out issue of alignment. Currently lo grabs alignment + from format, almost certainly "ought" to use the greater of the + format alignment and the :c:macro:`MPS_ALIGN` value. David Jones, + 1997-07-02. + + +Definitions +----------- + +:mps:tag:`def.leaf` A "leaf" object is an object that contains no +references, or an object all of whose references refer to roots. That +is, any references that the object has must refer to a priori alive +objects that are guaranteed not to move, hence the references do not +need fixing. + +:mps:tag:`def.grain` A grain (of some alignment) is a contiguous aligned +area of memory of the smallest size possible (which is the same size +as the alignment). + + +Requirements +------------ + +:mps:tag:`req.source` See req.dylan.fun.obj.alloc and +req.dylan.prot.ffi.access. + +:mps:tag:`req.leaf` The pool must manage formatted leaf objects (see +:mps:ref:`.def.leaf` above for a definition). This is intended to encompass +Dylan and C leaf objects. Dylan leaf objects have a reference to their +wrapper, but are still leaf objects (in the sense of :mps:ref:`.def.leaf`) +because the wrapper will be a root. + +:mps:tag:`req.nofault` The memory containing objects managed by the pool +must not be protected. The client must be allowed to access these +objects without using the MPS trampoline (the exception mechanism, +q.v.). + + +Overview +-------- + +:mps:tag:`overview` + +:mps:tag:`overview.ms` The LO Pool is a non-moving mark-and-sweep collector. + +:mps:tag:`overview.ms.justify` Mark-and-sweep pools are simpler than moving +pools. + +:mps:tag:`overview.alloc` Objects are allocated in the pool using the +reserve/commit protocol on allocation points. + +:mps:tag:`overview.format` The pool is formatted. The format of the objects +in the pool is specified at instantiation time, using an format object +derived from a variant A format (using variant A is overkill, see +:mps:ref:`.if.init` below) (see design.mps.format for excuse about calling the +variant 'A'). + + +Interface +--------- + +:mps:tag:`if.init` + +:mps:tag:`if.init.args` The init method for this class takes one extra +parameter in the vararg parameter list. + +:mps:tag:`if.init.format` The extra parameter should be an object of type +Format and should describe the format of the objects that are to be +allocated in the pool. + +:mps:tag:`if.init.format.use` The pool uses the skip and alignment slots of +the format. The skip method is used to determine the length of objects +(during reclaim). The alignment field is used to determine the +granularity at which memory should be managed. + +:mps:tag:`if.init.format.a` Currently only format variant A is supported +though clearly that is overkill as only skip and alignment are used. + + +Data structures +--------------- + +:mps:tag:`sig` The signature for the LO Pool Class is 0x51970b07 +(SIGLOPOoL). + +:mps:tag:`poolstruct` The class specific pool structure is:: + + typedef struct LOStruct { + PoolStruct poolStruct; /* generic pool structure */ + Format format; /* format for allocated objects */ + Shift alignShift; + Sig sig; /* impl.h.misc.sig */ + } LOStruct; + +:mps:tag:`poolstruct.format` This is the format of the objects that are +allocated in the pool. + +:mps:tag:`poolstruct.alignShift` This is shift used in alignment +computations. It is ``SizeLog2(pool->alignment).`` It can be used on +the right of a shift operator (``<<`` or ``>>``) to convert between a +number of bytes and a number of grains. + +:mps:tag:`loseg` Every segment is an instance of segment class :c:type:`LOSegClass`, a +subclass of :c:type:`GCSegClass`, and is an object of type :c:type:`LOSegStruct`. + +:mps:tag:`loseg.purpose` The purpose of the ``LOSeg`` structure is to +associate the bit tables used for recording allocation and mark +information with the segment. + +:mps:tag:`loseg.decl` The declaration of the structure is as follows:: + + typedef struct LOSegStruct { + GCSegStruct gcSegStruct; /* superclass fields must come first */ + LO lo; /* owning LO */ + BT mark; /* mark bit table */ + BT alloc; /* alloc bit table */ + Count free; /* number of free grains */ + Sig sig; /* impl.h.misc.sig */ + } LOSegStruct; + +:mps:tag:`loseg.sig` The signature for a loseg is 0x519705E9 (SIGLOSEG). + +:mps:tag:`loseg.lo` The lo field points to the LO structure that owns this +segment. + +:mps:tag:`loseg.bit` Bit Tables (see design.mps.bt) are used to record +allocation and mark information. This is relatively straightforward, +but might be inefficient in terms of space in some circumstances. + +:mps:tag:`loseg.mark` This is a Bit Table that is used to mark objects +during a trace. Each grain in the segment is associated with 1 bit in +this table. When :c:func:`LOFix()` (see :mps:ref:`.fun.fix` below) is called the +address is converted to a grain within the segment and the +corresponding bit in this table is set. + +:mps:tag:`loseg.alloc` This is a Bit Table that is used to record which +addresses are allocated. Addresses that are allocated and are not +buffered have their corresponding bit in this table set. If a bit in +this table is reset then either the address is free or is being +buffered. + +:mps:tag:`loseg.diagram` The following diagram is now obsolete. It's also +not very interesting - but I've left the sources in case anyone ever +gets around to updating it. tony 1999-12-16 + +[missing diagram] + + +Functions +--------- + +External +........ + +:mps:tag:`fun.init` + +:mps:tag:`fun.destroy` + +:mps:tag:`fun.buffer-fill` + +.. note:: + + Explain way in which buffers interact with the alloc table and how + it could be improved. + +:mps:tag:`fun.buffer-empty` + +:mps:tag:`fun.condemn` + +.. c:function:: Res LOFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) + +:mps:tag:`fun.fix` Fix treats references of most ranks much the same. There +is one mark table that records all marks. A reference of rank +``RankAMBIG`` is first checked to see if it is aligned to the pool +alignment and discarded if not. The reference is converted to a grain +number within the segment (by subtracting the segments' base from the +reference and then dividing by the grain size). The bit (the one +corresponding to the grain number) is set in the mark table. +Exception, for a weak reference (rank is ``RankWEAK``) the mark table +is checked and the reference is fixed to 0 if this address has not +been marked otherwise nothing happens. Note that there is no check +that the reference refers to a valid object boundary (which wouldn't +be a valid check in the case of ambiguous references anyway). + +.. c:function:: void LOReclaim(Pool pool, Trace trace, Seg seg) + +:mps:tag:`fun.reclaim` Derives the loseg from the seg, and calls +:c:func:`loSegReclaim()` (see :mps:ref:`.fun.segreclaim` below). + + +Internal +........ + +.. c:function:: void loSegReclaim(LOSeg loseg, Trace trace) + +:mps:tag:`fun.segreclaim` For all the contiguous allocated regions in the +segment it locates the boundaries of all the objects in that region by +repeatedly skipping (by calling ``format->skip``) from the beginning +of the region (the beginning of the region is guaranteed to coincide +with the beginning of an object). For each object it examines the bit +in the mark bit table that corresponds to the beginning of the object. +If that bit is set then the object has been marked as a result of a +previous call to :c:func:`LOFix()`, the object is preserved by doing +nothing. If that bit is not set then the object has not been marked +and should be reclaimed; the object is reclaimed by resetting the +appropriate range of bits in the segment's free bit table. + +.. note:: + + Special things happen for buffered segments. + + Explain how the marked variable is used to free segments. + + +Attachment +---------- + +[missing attachment "LOGROUP.CWK"] + + diff --git a/mps/manual/html/_sources/design/poolmfs.txt b/mps/manual/html/_sources/design/poolmfs.txt new file mode 100644 index 00000000000..c817b64fc91 --- /dev/null +++ b/mps/manual/html/_sources/design/poolmfs.txt @@ -0,0 +1,30 @@ +.. _design-poolmfs: + + +.. index:: + pair: MFS pool class; design + single: pool class; MFS design + + +MFS pool class +============== + +.. mps:prefix:: design.mps.poolmfs + pair: MFS pool class; design + single: pool class; MFS design + + +Overview +-------- + +MFS stands for "Manual Fixed Small". The MFS pool class manages +objects that are of a fixed size. It is intended to only manage small +objects efficiently. Storage is recycled manually by the client +programmer. + +A particular instance of an MFS Pool can manage objects only of a +single size, but different instances can manage objects of different +sizes. The size of object that an instance can manage is declared when +the instance is created. + + diff --git a/mps/manual/html/_sources/design/poolmrg.txt b/mps/manual/html/_sources/design/poolmrg.txt new file mode 100644 index 00000000000..853983cce87 --- /dev/null +++ b/mps/manual/html/_sources/design/poolmrg.txt @@ -0,0 +1,659 @@ +.. _design-poolmrg: + + +.. index:: + pair: MRG pool class; design + single: pool class; MRG design + + +MRG pool class +============== + +.. mps:prefix:: design.mps.poolmrg + pair: MRG pool class; design + single: pool class; MRG design + + +Introduction +------------ + +:mps:tag:`readership` Any MPS developer. + +:mps:tag:`intro` This is the design of the MRG (Manual Rank Guardian) pool +class. The MRG pool class is part of the MPS. The MRG pool class is +internal to the MPS (has no client interface) and is used to implement +finalization. + +:mps:tag:`source` Some of the techniques in paper.dbe93 ("Guardians in a +Generation-Based Garbage Collector") were used in this design. Some +analysis of this design (including various improvements and some more +in-depth justification) is in analysis.mps.poolmrg. That document +should be understood before changing this document. It is also helpful +to look at design.mps.finalize and design.mps.message. + + +Goals +----- + +:mps:tag:`goal.final` The MRG pool class should support all +requirements pertaining to finalization. + + +Requirements +------------ + +:mps:tag:`req` We have only one requirement pertaining to finalization: + +:mps:tag:`req.dylan.fun.finalization` Support the Dylan language-level +implementation of finalized objects: objects are registered, and are +finalized in random order when they would otherwise have died. Cycles +are broken at random places. There is no guarantee of promptness. + +:mps:tag:`req.general` However, finalization is a very common piece of +functionality that is provided by (sophisticated) memory managers, so +we can expect other clients to request this sort of functionality. + +:mps:tag:`anti-req` Is it required that the MRG pool class return +unused segments to the arena? MFS, for example, does not do this. MRG +will not do this in its initial implementation. + + +Terminology +----------- + +:mps:tag:`def.mrg` **MRG**: The MRG pool class's identifier will be MRG. +This stands for "Manual Rank Guardian". The pool is manually managed +and implements guardians for references of a particular rank +(currently just final). + +:mps:tag:`def.final.ref` **final reference**: A reference of rank final (see +design.mps.type.rank). + +:mps:tag:`def.final.object` **finalizable object**: An object is finalizable +with respect to a final reference if, since the creation of that +reference, there was a point in time when no references to the object +of lower (that is, stronger) rank were reachable from a root. + +:mps:tag:`def.final.object.note` Note that this means an object can be +finalizable even if it is now reachable from the root via exact +references. + +:mps:tag:`def.finalize` **finalize**: To finalize an object is to notify the +client that the object is finalizable. The client is presumed to be +interested in this information (typically it will apply some method to +the object). + +:mps:tag:`def.guardian` **guardian**: An object allocated in the MRG +Pool. A guardian contains exactly one final reference, and some fields +for the pool's internal use. Guardians are used to implement a +finalization mechanism. + + +Overview +-------- + +:mps:tag:`over` The MRG pool class is a pool class in the MPS. It is +intended to provide the functionality of "finalization". + +:mps:tag:`over.internal` The MRG pool class is internal to the MPM: it +is not intended to have a client interface. Clients are expected to +access the functionality provided by this pool (finalization) using a +separate MPS finalization interface (design.mps.finalize). + +:mps:tag:`over.one-size` The MRG pool class manages objects of a single +size, each object containing a single reference of rank final. + +:mps:tag:`over.one-size.justify` This is all that is necessary to meet our +requirements for finalization. Whenever an object is registered for +finalization, it is sufficient to create a single reference of rank +final to it. + +:mps:tag:`over.queue` A pool maintains a list of live guardian objects, +called (for historical reasons) the "entry" list. + +:mps:tag:`over.queue.free` The pool also maintains a list of free guardian +objects called the "free" list. + +:mps:tag:`over.queue.exit.not` There used to be an "exit" list, but this is +now historical and there shouldn't be any current references to it. + +:mps:tag:`over.alloc` When guardians are allocated, they are placed on the +entry list. Guardians on the entry list refer to objects that have not +yet been shown to be finalizable (either the object has references of +lower rank than final to it, or the MPS has not yet got round to +determining that the object is finalizable). + +:mps:tag:`over.message.create` When a guardian is discovered to refer to a +finalizable object it is removed from the entry list and becomes a +message on the arena's messages queue. + +:mps:tag:`over.message.deliver` When the MPS client receives the message the +message system arranges for the message to be destroyed and the pool +reclaims the storage associated with the guardian/message. + +:mps:tag:`over.scan` When the pool is scanned at rank final each reference +will be fixed. If the reference is to an unmarked object (before the +fix), then the object must now be finalizable. In this case the +containing guardian will be removed from the entry list and posted as +a message. + +:mps:tag:`over.scan.justify` The scanning process is a crucial step +necessary for implementing finalization. It is the means by which the +MPS detects that objects are finalizable. + +:mps:tag:`over.message` ``PoolClassMRG`` implements a :c:type:`MessageClass` (see +design.mps.message). All the messages are of one ``MessageType``. This +type is ``MessageTypeFinalization``. Messages are created when objects +are discovered to be finalizable and destroyed when the MPS client has +received the message. + +:mps:tag:`over.message.justify` Messages provide a means for the MPS to +communicate with its client. Notification of finalization is just such +a communication. Messages allow the MPS to inform the client of +finalization events when it is convenient for the MPS to do so (i.e. +not in PageFault context). + +:mps:tag:`over.manual` Objects in the MRG pool are manually managed. + +:mps:tag:`over.manual.alloc` They are allocated by :c:func:`ArenaFinalize()` when +objects are registered for finalization. + +:mps:tag:`over.manual.free` They are freed when the associated message is +destroyed. + +:mps:tag:`over.manual.justify` The lifetime of a guardian object is very +easy to determine so manual memory management is appropriate. + + +Protocols +--------- + +Object Registration +................... + +:mps:tag:`protocol.register` There is a protocol by which objects can be +registered for finalization. This protocol is handled by the arena +module on behalf of finalization. see +design.mps.finalize.int.finalize. + + +Finalizer execution +................... + +:mps:tag:`protocol.finalizer` If an object is proven to be finalizable then +a message to this effect will eventually be posted. A client can +receive the message, determine what to do about it, and do it. +Typically this would involve calling the finalization method for the +object, and deleting the message. Once the message is deleted, the +object may become recyclable. + + +Setup / destroy +............... + +:mps:tag:`protocol.life` An instance of PoolClassMRG is needed in order to +support finalization, it is called the "final" pool and is attached to +the arena (see design.mps.finalize.int.arena.struct). + +:mps:tag:`protocol.life.birth` The final pool is created lazily by +:c:func:`ArenaFinalize()`. + +:mps:tag:`protocol.life.death` The final pool is destroyed during +:c:func:`ArenaDestroy()`. + + +Data structures +--------------- + +:mps:tag:`guardian` The guardian + +:mps:tag:`guardian.over` A guardian is an object used to manage the +references and other data structures that are used by the pool in +order to keep track of which objects are registered for finalization, +which ones have been finalized, and so on. + +:mps:tag:`guardian.state` A guardian can be in one of four states: + +:mps:tag:`guardian.state.enum` The states are Free, Prefinal, Final, +PostFinal (referred to as MRGGuardianFree, etc. in the +implementation). + +1. :mps:tag:`guardian.state.free` The guardian is free, meaning that it is + on the free list for the pool and available for allocation. + +2. :mps:tag:`guardian.state.prefinal` The guardian is allocated, and refers + to an object that has not yet been discovered to be finalizable. It + is on the entry list for the pool. + +3. :mps:tag:`guardian.state.final` The guardian is allocated, and refers to + an object that has been shown to be finalizable; this state + corresponds to the existence of a message. + +4. :mps:tag:`guardian.state.postfinal` This state is only used briefly and + is entirely internal to the pool; the guardian enters this state + just after the associated message has been destroyed (which happens + when the client receives the message) and will be freed immediately + (whereupon it will enter the Free state). This state is used for + checking only (so that MRGFree can check that only guardians in + this state are being freed). + +:mps:tag:`guardian.life-cycle` Guardians go through the following state life-cycle: Free ⟶ Prefinal ⟶ Final ⟶ Postfinal ⟶ Free. + +:mps:tag:`guardian.two-part` A guardian is a structure consisting abstractly +of a link part and a reference part. Concretely, the link part is a +:c:type:`LinkPartStruct`, and the reference part is a :c:type:`RefPartStruct` +(which is just a :c:type:`Word`). The link part is used by the pool, the +reference part forms the object visible to clients of the pool. The +reference part is the reference of ``RankFINAL`` that refers to +objects registered for finalization and is how the MPS detects +finalizable objects. + +:mps:tag:`guardian.two-part.union` The :c:type:`LinkPartStruct` is a discriminated +union of a :c:type:`RingStruct` and a :c:type:`MessageStruct`. The :c:type:`RingStruct` +is used when the guardian is either Free or Prefinal. The +MessageStruct is used when the guardian is Final. Neither part of the +union is used when the guardian is in the Postfinal state. + +:mps:tag:`guardian.two-part.justify` This may seem a little profligate with +space, but this is okay as we are not required to make finalization +extremely space efficient. + +:mps:tag:`guardian.parts.separate` The two parts will be stored in separate +segments. + +:mps:tag:`guardian.parts.separate.justify` This is so that the data +structures the pool uses to manage the objects can be separated from +the objects themselves. This avoids the pool having to manipulate data +structures that are on shielded segments +(analysis.mps.poolmrg.hazard.shield). + +:mps:tag:`guardian.assoc` Ref part number *n* (from the beginning of the +segment) in one segment will correspond with link part number *n* in +another segment. The association between the two segments will be +managed by the additional fields in pool-specific segment subclasses +(see :mps:ref:`.mrgseg`). + +:mps:tag:`guardian.ref` Guardians that are either Prefinal or Final are live +and have valid references (possibly :c:macro:`NULL`) in their ref parts. +Guardians that are free are dead and always have :c:macro:`NULL` in their ref +parts (see :mps:ref:`.free.overwrite` and :mps:ref:`.scan.free`). + +:mps:tag:`guardian.ref.free` When freeing an object, it is a pointer to the +reference part that will be passed (internally in the pool). + +:mps:tag:`guardian.init` Guardians are initialized when the pool is grown +(:mps:ref:`.alloc.grow`). The initial state has the ref part :c:macro:`NULL` and the +link part is attached to the free ring. Freeing an object returns a +guardian to its initial state. + +:mps:tag:`poolstruct` The Pool structure, :c:type:`MRGStruct` will have: + +- :mps:tag:`poolstruct.entry` the head of the entry list. + +- :mps:tag:`poolstruct.free` the head of the free list. + +- :mps:tag:`poolstruct.rings` The entry list, the exit list, and the free + list will each be implemented as a :c:type:`Ring`. Each ring will be + maintained using the link part of the guardian. + + :mps:tag:`poolstruct.rings.justify` This is because rings are convenient to + use and are well tested. It is possible to implement all three lists + using a singly linked list, but the saving is certainly not worth + making at this stage. + +- :mps:tag:`poolstruct.refring` a ring of "ref" segments in use for links or + messages (see .mrgseg.ref.mrgring below). + +- :mps:tag:`poolstruct.extend` a precalculated ``extendBy`` field (see + :mps:ref:`.init.extend`). This value is used to determine how large a + segment should be requested from the arena for the reference part + segment when the pool needs to grow (see :mps:ref:`.alloc.grow.size`). + + :mps:tag:`poolstruct.extend.justify` Calculating a reasonable value for this + once and remembering it simplifies the allocation (:mps:ref:`.alloc.grow`). + +:mps:tag:`poolstruct.init` poolstructs are initialized once for each pool +instance by :c:func:`MRGInit()` (:mps:ref:`.init`). The initial state has all the +rings initialized to singleton rings, and the ``extendBy`` field +initialized to some value (see :mps:ref:`.init.extend`). + +:mps:tag:`mrgseg` The pool defines two segment subclasses: +:c:type:`MRGRefSegClass` and :c:type:`MRGLinkSegClass`. Segments of the former +class will be used to store the ref parts of guardians, segments of +the latter will be used to store the link parts of guardians (see +:mps:ref:`.guardian.two-part`). Segments are always allocated in pairs, with +one of each class, by the function :c:func:`MRGSegPairCreate()`. Each +segment contains a link to its pair. + +:mps:tag:`mrgseg.ref` :c:type:`MRGRefSegClass` is a subclass of :c:type:`GCSegClass`. +Instances are of type ``MRGRefSeg``, and contain: + +- :mps:tag:`mrgseg.ref.mrgring` a field for the ring of ref part segments in + the pool. + +- :mps:tag:`mrgseg.ref.linkseg` a pointer to the paired link segment. + +- :mps:tag:`mrgseg.ref.grey` a set describing the greyness of the segment for each trace. + +:mps:tag:`mrgseg.ref.init` A segment is created and initialized once every +time the pool is grown (:mps:ref:`.alloc.grow`). The initial state has the +segment ring node initialized and attached to the pool's segment ring, +the linkseg field points to the relevant link segment, the grey field +is initialized such that the segment is not grey for all traces. + +:mps:tag:`mrgseg.link` :c:type:`MRGLinkSegClass` is a subclass of :c:type:`SegClass`. +Instances are of type ``MRGLinkSeg``, and contain: + +- :mps:tag:`mrgseg.link.refseg` a pointer to the paired ref segment. This + may be :c:macro:`NULL` during initialization, while the pairing is being + established. + +- :mps:tag:`mrgseg.link.init` The initial state has the ``linkseg`` field + pointing to the relevant ref segment. + + +Functions +--------- + +.. c:function:: Bool MRGCheck(MRG mrg) + +:mps:tag:`check` Check the signatures, the class, and each field of the +:c:type:`MRGStruct`. Each field is checked as being appropriate for its +type. + +:mps:tag:`check.justify` There are no non-trivial invariants that can +be easily checked. + +.. c:function:: Res MRGRegister(Pool pool, Ref ref) + +:mps:tag:`alloc` Add a guardian for ``ref``. + +:mps:tag:`alloc.grow` If the free list is empty then two new segments are +allocated and the free list filled up from them (note that the +reference fields of the new guardians will need to be overwritten with +:c:macro:`NULL`, see :mps:ref:`.free.overwrite`) + +:mps:tag:`alloc.grow.size` The size of the reference part segment will be +the pool's ``extendBy`` (:mps:ref:`.poolstruct.extend`) value. The link part +segment will be whatever size is necessary to accommodate *N* link +parts, where *N* is the number of reference parts that fit in the +reference part segment. + +:mps:tag:`alloc.error` If any of the requests for more resource (there are +two; one for each of two segments) fail then the successful requests +will be retracted and the result code from the failing request will be +returned. + +:mps:tag:`alloc.pop` :c:func:`MRGRegister()` pops a ring node off the free list, +and add it to the entry list. + +.. c:function:: Res MRGDeregister(Pool pool, Ref obj) + +:mps:tag:`free` Remove the guardian from the message queue and add it to the +free list. + +:mps:tag:`free.push` The guardian will simply be added to the front of the +free list (that is, no keeping the free list in address order or +anything like that). + +:mps:tag:`free.inadequate` No attempt will be made to return unused free +segments to the arena (although see +analysis.mps.poolmrg.improve.free.* for suggestions). + +:mps:tag:`free.overwrite` :c:func:`MRGDeregister()` also writes over the reference +with :c:macro:`NULL`. :mps:tag:`free.overwrite.justify` This is so that when the +segment is subsequently scanned (:mps:ref:`.scan.free`), the reference that +used to be in the object is not accidentally fixed. + +.. c:function:: Res MRGInit(Pool pool, ArgList args) + +:mps:tag:`init` Initializes the entry list, the free ring, the ref ring, and +the ``extendBy`` field. + +:mps:tag:`init.extend` The ``extendBy`` field is initialized to one +:c:func:`ArenaAlign()` (usually a page). + +:mps:tag:`init.extend.justify` This is adequate as the pool is not expected +to grow very quickly. + +.. c:function:: void MRGFinish(Pool pool) + +:mps:tag:`finish` Iterate over all the segments, returning all the segments +to the arena. + +.. c:function:: Res MRGScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) + +:mps:tag:`scan` :c:func:`MRGScan()` scans a segment. + +:mps:tag:`scan.trivial` Scan will do nothing (that is, return immediately) +if the tracing rank is anything other than final. + +.. note:: + + This optimization is missing. impl.c.trace.scan.conservative is + not a problem because there are no faults on these segs, because + there are no references into them. But that's why :c:func:`TraceScan()` + can't do it. Pekka P. Pirinen, 1997-09-19. + +:mps:tag:`scan.trivial.justify` If the rank is lower than final then +scanning is detrimental, it will only delay finalization. If the rank +is higher than final there is nothing to do, the pool only contains +final references. + +:mps:tag:`scan.guardians` :c:func:`MRGScan()` will iterate over all guardians in +the segment. Every guardian's reference will be fixed (:mps:tag:`scan.free` +note that guardians that are on the free list have :c:macro:`NULL` in their +reference part). + +:mps:tag:`scan.wasold` If the object referred to had not been fixed +previously (that is, was unmarked) then the object is not referenced +by a reference of a lower rank (than ``RankFINAL``) and hence is +finalizable. + +:mps:tag:`scan.finalize` The guardian will be finalized. This entails moving +the guardian from state Prefinal to Final; it is removed from the +entry list and initialized as a message and posted on the arena's +message queue. + +:mps:tag:`scan.finalize.idempotent` In fact this will only happen if the +guardian has not already been finalized (which is determined by +examining the state of the guardian). + +:mps:tag:`scan.unordered` Because scanning occurs a segment at a time, the +order in which objects are finalized is "random" (it cannot be +predicted by considering only the references between objects +registered for finalization). See +analysis.mps.poolmrg.improve.semantics for how this can be improved. + +:mps:tag:`scan.unordered.justify` Unordered finalization is all that is +required. + +See analysis.mps.poolmrg.improve.scan.nomove for a suggested +improvement that avoids redundant unlinking and relinking. + +.. c:function:: Res MRGDescribe(Pool pool, mps_lib_FILE *stream) + +:mps:tag:`describe` Describes an MRG pool. Iterates along each of the entry +and exit lists and prints the guardians in each. The location of the +guardian and the value of the reference in it will be printed out. + +:mps:tag:`functions.unused` All of these will be unused: :c:func:`BufferInit()`, +:c:func:`BufferFill()`, :c:func:`BufferEmpty()`, :c:func:`BufferFinish()`, +:c:func:`TraceBegin()`, :c:func:`TraceCondemn()`, :c:func:`PoolFix()`, :c:func:`PoolReclaim()`, :c:func:`TraceEnd()`. + +:mps:tag:`functions.trivial` The Grey method of the pool class will be +:c:func:`PoolTrivGrey()`, this pool has no further bookkeeping to perform +for grey segments. + + +Transgressions +-------------- + +:mps:tag:`trans.no-finish` The MRG pool does not trouble itself to tidy up +its internal rings properly when being destroyed. + +:mps:tag:`trans.free-seg` No attempt is made to release free segments to the +arena. A suggested strategy for this is as follows: + +- Add a count of free guardians to each segment, and maintain it in + appropriate places. + +- Add a free segment ring to the pool. + +- In :c:func:`MRGRefSegScan()`, if the segment is entirely free, don't scan + it, but instead detach its links from the free ring, and move the + segment to the free segment ring. + +- At some appropriate point, such as the end of :c:func:`MRGAlloc()`, + destroy free segments. + +- In :c:func:`MRGAlloc()`, if there are no free guardians, check the free + segment ring before creating a new pair of segments. Note that this + algorithm would give some slight measure of segment hysteresis. It + is not the place of the pool to support general segment hysteresis. + + +Future +------ + +:mps:tag:`future.array` In future, for speed or simplicity, this pool could +be rewritten to use an array. See mail.gavinm.1997-09-04.13-08(0). + + +Tests +----- + +.. note:: + + This section is utterly out of date. Pekka P. Pirinen, 1997-09-19. + +:mps:tag:`test` The test impl.c.finalcv is similar to the weakness test (see +design.mps.weakness, impl.c.weakcv). + + +Functionality +............. + +This is the functionality to be tested: + +- :mps:tag:`fun.alloc` Can allocate objects. +- :mps:tag:`fun.free` Can free objects that were allocated. +- :mps:tag:`prot.write` Can write a reference into an allocated object. +- :mps:tag:`prot.read` Can read the reference from an allocated object. +- :mps:tag:`promise.faithful` A reference stored in an allocated object will + continue to refer to the same object. +- :mps:tag:`promise.live` A reference stored in an allocated object will + preserve the object referred to. +- :mps:tag:`promise.unreachable` Any objects referred to in finalization + messages are not (at the time of reading the message) reachable via + a chain of ambiguous or exact references. (we will not be able to + test this at first as there is no messaging interface) +- :mps:tag:`promise.try` The pool will make a "good faith" effort to + finalize objects that are not reachable via a chain of ambiguous or + exact references. + +Attributes +.......... + +The following attributes will be tested: + +- :mps:tag:`attr.none` There are no attribute requirements. + +Implementation +.............. + +The test will simply allocate a number of objects in the AMC pool and +finalize each one, throwing away the reference to the objects. Churn. + +:mps:tag:`test.mpm` The test will use the MPM interface (impl.h.mpm). + +:mps:tag:`test.mpm.justify` This is because it is not intended to provide an +MPS interface to this pool directly, and the MPS interface to +finalization has not been written yet (impl.h.mps). + +:mps:tag:`test.mpm.change` Later on it may use the MPS interface, in which +case, where the following text refers to allocating objects in the MRG +pool it will need adjusting. + +:mps:tag:`test.two-pools` The test will use two pools, an AMC pool, and an +MRG pool. + +:mps:tag:`test.alloc` A number of objects will be allocated in the MRG pool. + +:mps:tag:`test.free` They will then be freed. This will test :mps:ref:`.fun.alloc` +and :mps:ref:`.fun.free`, although not very much. + +:mps:tag:`test.rw.a` An object, "A", will be allocated in the AMC pool, a +reference to it will be kept in a root. + +:mps:tag:`test.rw.alloc` A number of objects will be allocated in the MRG +pool. + +:mps:tag:`test.rw.write` A reference to "A" will be written into each +object. + +:mps:tag:`test.rw.read` The reference in each object will be read and +checked to see if it refers to "A". + +:mps:tag:`test.rw.free` All the objects will be freed. + +:mps:tag:`test.rw.drop` The reference to "A" will be dropped. This will test +:mps:ref:`.prot.write` and :mps:ref:`.prot.read`. + +:mps:tag:`test.promise.fl.alloc` A number of objects will be allocated in +the AMC pool. + +:mps:tag:`test.promise.fl.tag` Each object will be tagged uniquely. + +:mps:tag:`test.promise.fl.refer` a reference to it will be stored in an +object allocated in the MRG pool. + +:mps:tag:`test.promise.fl.churn` A large amount of garbage will be allocated +in the AMC pool. Regularly, whilst this garbage is being allocated, a +check will be performed that all the objects allocated in the MRG pool +refer to valid objects and that they still refer to the same objects. +All objects from the MRG pool will then be freed (thus dropping all +references to the AMC objects). This will test :mps:ref:`.promise.faithful` +and :mps:ref:`.promise.live`. + +:mps:tag:`test.promise.ut.not` The following part of the test has not +implemented. This is because the messaging system has not yet been +implemented. + +:mps:tag:`test.promise.ut.alloc` A number of objects will be allocated in +the AMC pool. + +:mps:tag:`test.promise.ut.refer` Each object will be referred to by a root +and also referred to by an object allocated in the MRG pool. + +:mps:tag:`test.promise.ut.drop` References to a random selection of the +objects from the AMC pool will be deleted from the root. + +:mps:tag:`test.promise.ut.churn` A large amount of garbage will be allocated +in the AMC pool. + +:mps:tag:`test.promise.ut.message` The message interface will be used to +receive finalization messages. + +:mps:tag:`test.promise.ut.final.check` For each finalization message +received it will check that the object referenced in the message is +not referred to in the root. + +:mps:tag:`test.promise.ut.nofinal.check` After some amount of garbage has +been allocated it will check to see if any objects are not in the root +and haven't been finalized. This will test :mps:ref:`.promise.unreachable` and +:mps:ref:`.promise.try`. + + +Notes +----- + +:mps:tag:`access.inadequate` :c:func:`PoolAccess()` will scan segments at +`RankEXACT``. Really it should be scanned at whatever the minimum rank +of all grey segments is (the trace rank phase), however there is no +way to find this out. As a consequence we will sometimes scan pages at +``RankEXACT`` when the pages could have been scanned at ``RankFINAL``. +This means that finalization of some objects may sometimes get +delayed. + + diff --git a/mps/manual/html/_sources/design/poolmv.txt b/mps/manual/html/_sources/design/poolmv.txt new file mode 100644 index 00000000000..dd15e4381af --- /dev/null +++ b/mps/manual/html/_sources/design/poolmv.txt @@ -0,0 +1,18 @@ +.. _design-poolmv: + + +MV pool class +============= + +.. mps:prefix:: design.mps.poolmv + + +Implementation +-------------- + +:mps:tag:`lost` It is possible for MV to "lose" memory when freeing an +objects. This happens when an extra block descriptor is needed (ie the +interior of a block is being freed) and the call to allocate the block +fails. + + diff --git a/mps/manual/html/_sources/design/poolmvff.txt b/mps/manual/html/_sources/design/poolmvff.txt index 7c7e50fe354..9494380820e 100644 --- a/mps/manual/html/_sources/design/poolmvff.txt +++ b/mps/manual/html/_sources/design/poolmvff.txt @@ -1,7 +1,141 @@ -.. index:: - pair: MVFF; design - single: pool; MVFF design - .. _design-poolmvff: -.. include:: ../../converted/poolmvff.rst + +.. index:: + pair: MVFF pool class; design + single: pool class; MVFF design + + +MVFF pool class +=============== + +.. mps:prefix:: design.mps.poolmvff + pair: MVFF pool class; design + single: pool class; MVFF design + + +Introduction +------------ + +:mps:tag:`intro` This is the design of the MVFF (Manual Variable First-Fit) +pool class. This pool implements a first (or last) fit policy for +variable-sized manually-managed objects, with control over first/last, +segment preference high/low, and slot fit low/high. + +The pool was created in a response to a belief that the ScriptWorks +EPDL/EPDR's first fit policy is beneficial for some classes of client +behaviour, but the performance of a linear free list was unacceptable. + + +Overview +-------- + +:mps:tag:`over` This pool implements certain variants of the address-ordered +first-fit policy. The implementation allows allocation across segment +boundaries. + +:mps:tag:`over.buffer` Buffered allocation is also supported, but in that +case, the buffer-filling policy is worst-fit. Buffered and unbuffered +allocation can be used at the same time, but in that case, the first +ap must be created before any allocations. + +:mps:tag:`over.buffer.class` The pool uses the simplest buffer class, +BufferClass. This is appropriate since these buffers don't attach to +segments, and hence don't constrain buffered regions to lie within +segment boundaries. + +:mps:tag:`over.segments` The pool uses the simplest segment class +(SegClass). There's no need for anything more complex. + + +Methods +------- + +:mps:tag:`method` The MVFF pool supports the following methods: + +.. c:function:: Res MVFFInit(Pool pool, Args arg) + +:mps:tag:`method.init` This takes six `keyword arguments`_: + +.. _`keyword arguments`: keyword-arguments + +================================== ============================================ +Keyword argument Description +================================== ============================================ +:c:macro:`MPS_KEY_EXTEND_BY` The segment size. +:c:macro:`MPS_KEY_MEAN_SIZE` The average object size. +:c:macro:`MPS_KEY_ALIGN` The alignment of allocations and frees. + Must be at least ``sizeof(void *)``. +:c:macro:`MPS_KEY_MVFF_SLOT_HIGH` Whether to allocate objects at the end of + free blocks found, as opposed to at + the start (for unbuffered + allocation). +:c:macro:`MPS_KEY_MVFF_ARENA_HIGH` Whether to express ``SegPrefHIGH`` + to the arena, as opposed to + ``SegPrefLOW``. +:c:macro:`MPS_KEY_MVFF_FIRST_FIT` whether to use the suitable block of lowest + address, as opposed to the highest + (for unbuffered allocation) +================================== ============================================ + +:mps:tag:`method.init.epdl` To simulate the EPDL pool, specify ``extendBy``, +``avgSize``, and ``maxSize`` as normal, and use ``slotHigh=FALSE``, +``arenaHigh=FALSE``, ``firstFit=TRUE``. + +:mps:tag:`method.init.epdr` To simulate the EPDR pool, specify ``extendBy``, +``avgSize``, and ``maxSize`` as normal, and use ``slotHigh=TRUE``, +``arenaHigh=TRUE``, ``firstFit=TRUE``. + +:mps:tag:`method.init.other` The performance characteristics of other +combinations are unknown. + +:mps:tag:`method.finish` The :c:func:`PoolFinish()` method, + +:mps:tag:`method.alloc` :c:func:`PoolAlloc()` and :c:func:`PoolFree()` methods are +supported, implementing the policy set by the pool params (see +:mps:ref:`.method.init`). + +:mps:tag:`method.describe` The usual describe method. + +:mps:tag:`method.buffer` The buffer methods implement a worst-fit fill +strategy. + + +External Functions +------------------ + +:mps:tag:`function` MVFF supports the following external functions: + +:mps:tag:`function.free-size` :c:func:`mps_mvff_free_size()` returns the total +size of free space in segments allocated to the MVFF pool instance. + +:mps:tag:`function.size` :c:func:`mps_mvff_size()` returns the total memory used +by pool segments, whether free or allocated. + +:mps:tag:`function.class` :c:func:`mps_class_mvff()` returns the class object for +the pool class, to be used in pool creation. + + +Implementation +-------------- + +:mps:tag:`impl.free-list` The pool stores its free list in a CBS (see +`design.mps.cbs `_). It uses the CBS's mayUseInline facility to +avoid running out of memory to store the free list. This is the reason +for the alignment restriction above. + + +Details +------- + +:mps:tag:`design.seg-size` When adding a segment, we use extendBy as the +segment size unless the object won't fit, in which case we use the +object size (in both cases we align up). + +:mps:tag:`design.seg-fail` If allocating a segment fails, we try again with +a segment size just large enough for the object we're allocating. This +is in response to `request.mps.170186`_. + +.. _`request.mps.170186`: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/mps/170186/ + + diff --git a/mps/manual/html/_sources/design/prot.txt b/mps/manual/html/_sources/design/prot.txt index f2a8d3a2ede..a3b9ad04a02 100644 --- a/mps/manual/html/_sources/design/prot.txt +++ b/mps/manual/html/_sources/design/prot.txt @@ -1,6 +1,87 @@ +.. _design-prot: + + .. index:: pair: protection interface; design -.. _design-prot: -.. include:: ../../converted/prot.rst +The protection module +===================== + +.. mps:prefix:: design.mps.prot + + +Introduction +------------ + +:mps:tag:`intro` This is the generic design of the Protection Module. The +protection module provides protection services to other parts of the +MPS. It is expected that different operating systems will have +different implementations of this module. + +:mps:tag:`readership` Any MPS developer. + + +Interface +--------- + +.. c:function:: void ProtSetup(void) + +:mps:tag:`if.setup` :c:func:`ProtSetup()` will be called exactly once (per +process). It will be called as part of the initialization of the first +space that is created. It should arrange for the setup and +initialization of any datastructures or services that are necessary in +order to implement the protection module. (On UNIX it expected that it +will install a signal handler, on Windows it will do nothing) + +.. c:function:: void ProtSet(Addr base, Addr limit, AccessSet mode) + +:mps:tag:`if.set` :c:func:`ProtSet()` should set the protection of the memory +between base and limit, including base, but not including limit (ie +the half-open interval [base,limit)) to that specified by mode. The +mode parameter should have the ``AccessWRITE`` bit set if write +accesses to the page are to be forbidden, and should have the +``AccessREAD`` bit set if read accesses to the page are to be +forbidden. A request to forbid read accesses (that is, ``AccessREAD`` +is set) may also forbid write accesses, but read accesses will not be +forbidden unless ``AccessREAD`` is set. + +.. c:function:: void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), void *p, size_t s) + +:mps:tag:`if.tramp` [undocumented] + +.. c:function:: void ProtSync(Space space) + +:mps:tag:`if.sync` :c:func:`ProtSync()` is called to ensure that the actual +protection of each segment (as determined by the OS) is in accordance +with the segments's ``pm`` field. + +.. c:type:: struct MutatorFaultContextStruct *MutatorFaultContext + +:mps:tag:`if.context-type` This abstract type is implemented by the +protection module (impl.c.prot*). It represents the continuation of +the mutator which is restored after a mutator fault has been handled. +The functions :c:func:`ProtCanStepInstruction()` (:mps:ref:`.if.canstep` below) and +:c:func:`ProtStepInstruction()` (:mps:ref:`.if.step` below) inspect and manipulate +the context. + +.. c:function:: Bool ProtCanStepInstruction(MutatorFaultContext context) + +:mps:tag:`if.canstep` Examines the context to determine whether the +protection module can single-step the instruction which is causing the +fault. Should return :c:macro:`TRUE` if and only if the instruction can be +single-stepped (that is, :c:func:`ProtStepInstruction()` can be called). + +.. c:function:: Bool Res ProtStepInstruction(MutatorFaultContext context) + +:mps:tag:`if.step` Single-steps the instruction which is causing the fault. +This function should only be called if :c:func:`ProtCanStepInstruction()` +applied to the context returned :c:macro:`TRUE`. It should return +``ResUNIMPL`` if the instruction cannot be single-stepped. It should +return ``ResOK`` if the instruction is single-stepped. + +The mutator context will be updated by the emulation/execution of the +instruction such that resuming the mutator will not cause the +instruction which was causing the fault to be executed. + + diff --git a/mps/manual/html/_sources/design/protan.txt b/mps/manual/html/_sources/design/protan.txt new file mode 100644 index 00000000000..9ceb972aad1 --- /dev/null +++ b/mps/manual/html/_sources/design/protan.txt @@ -0,0 +1,81 @@ +.. _design-protan: + + +.. index:: + pair: ANSI; protection interface design + pair: ANSI protection interface; design + + +ANSI implementation of protection module +======================================== + +.. mps:prefix:: design.mps.protan + pair: ANSI; protection interface design + pair: ANSI protection interface; design + + +Introduction +------------ + +:mps:tag:`readership` Any MPS developer. + +:mps:tag:`intro` This is the design for the ANSI implementation of the +protection module. + + +Requirements +------------ + +:mps:tag:`req.test` This module is required for testing. Particularly on +platforms where no real implementation of the protection module +exists. + +:mps:tag:`req.rapid-port` This module is required for rapid porting. It +should enable a developer to port a minimally useful configuration of +the MPS to new platforms very quickly. + + +Overview +-------- + +:mps:tag:`overview` Most of the functions in the module do nothing. The +exception is :c:func:`ProtSync()` which traverses over all segments in the +arena and simulates an access to each segment that has any protection +on it. This means that this module depends on certain fields in the +segment structure. + +:mps:tag:`overview.noos` No operating system specific (or even ANSI hosted +specific) code is in this module. It can therefore be used on any +platform, particularly where no real implementation of the module +exists. It satisfies :mps:ref:`.req.test` and :mps:ref:`.req.rapid-port` in this way. + + +Functions +--------- + +:mps:tag:`fun.protsetup` :c:func:`ProtSetup()` does nothing as there is nothing to +do (under UNIX we might expect the protection module to install one or +more signal handlers at this pointer, but that is not appropriate for +the ANSI implementation). Of course, we can't have an empty function +body, so there is a ``NOOP;`` here. + +:mps:tag:`fun.sync` :c:func:`ProtSync()` is called to ensure that the actual +protection of each segment (as determined by the OS) is in accordance +with the segments's pm field. In the ANSI implementation we have no +way of changing the protection of a segment, so instead we generate +faults on all protected segments in the assumption that that will +remove the protection on segments. + +:mps:tag:`fun.sync.how` Continually loops over all the segments until it +finds that all segments have no protection. + +:mps:tag:`fun.sync.seg` If it finds a segment that is protected then +:c:func:`PoolAccess()` is called on that segment's pool and with that +segment. The call to :c:func:`PoolAccess()` is wrapped with a +:c:func:`ShieldEnter()` and :c:func:`ShieldLeave()` thereby giving the pool the +illusion that the fault was generated outside the MM. This depends on +being able to determine the protection of a segment (using the ``pm`` +field), on being able to call :c:func:`ShieldEnter()` and :c:func:`ShieldLeave()`, +and on being able to call :c:func:`PoolAccess()`. + + diff --git a/mps/manual/html/_sources/design/protli.txt b/mps/manual/html/_sources/design/protli.txt new file mode 100644 index 00000000000..a10e4077e19 --- /dev/null +++ b/mps/manual/html/_sources/design/protli.txt @@ -0,0 +1,198 @@ +.. _design-protli: + + +.. index:: + pair: Linux; protection interface design + pair: Linux protection interface; design + + +Linux implementation of protection module +========================================= + +.. mps:prefix:: design.mps.protli + pair: Linux; protection interface design + pair: Linux protection interface; design + + +Introduction +------------ + +:mps:tag:`readership` Any MPS developer + +:mps:tag:`intro` This is the design of the Linux implementation of the +protection module. It makes use of various services provided by Linux. +It is intended to work with LinuxThreads. + + +Requirements +------------ + +:mps:tag:`req.general` Required to implement the general protection +interface defined in design.mps.prot.if. + + +Data structures +--------------- + +:mps:tag:`data.signext` This is static. Because that is the only +communications channel available to signal handlers. + +.. note:: + + Write a little more here. + + +Functions +--------- + +:mps:tag:`fun.setup` :c:func:`ProtSetup()` installs a signal handler for the signal +:c:macro:`SIGSEGV` to catch and handle protection faults (this handler is the +function :c:func:`sigHandle()`, see :mps:ref:`.fun.sighandle`). The previous handler +is recorded (in the variable ``sigNext``, see :mps:ref:`.data.signext`) so +that it can be reached from :c:func:`sigHandle()` if it fails to handle the +fault. + +:mps:tag:`fun.setup.problem` The problem with this approach is that we can't +honour the wishes of the ``sigvec(2)`` entry for the previous handler +(in terms of masks in particular). + +:mps:tag:`improve.sigvec` What if when we want to pass on the signal instead +of calling the handler we call :c:func:`sigvec()` with the old entry and use +:c:func:`kill()` to send the signal to ourselves and then restore our +handler using :c:func:`sigvec()` again? + +.. note:: + + Need more detail and analysis here. + +:mps:tag:`fun.set` :c:func:`ProtSet()` uses :c:func:`mprotect()` to adjust the +protection for pages. + +:mps:tag:`fun.set.convert` The requested protection (which is expressed in +the ``mode`` parameter, see design.mps.prot.if.set) is translated into +an operating system protection. If read accesses are to be forbidden +then all accesses are forbidden, this is done by setting the +protection of the page to :c:macro:`PROT_NONE`. If write accesses are to be +forbidden (and not read accesses) then write accesses are forbidden +and read accesses are allowed, this is done by setting the protection +of the page to ``PROT_READ|PROT_EXEC``. Otherwise (all access are +okay), the protection is set to ``PROT_READ|PROT_WRITE|PROT_EXEC``. + +:mps:tag:`fun.set.assume.mprotect` We assume that the call to :c:func:`mprotect()` +always succeeds. + +:mps:tag:`fun.set.assume.mprotect` This is because we should always call the +function with valid arguments (aligned, references to mapped pages, +and with an access that is compatible with the access of the +underlying object). + +:mps:tag:`fun.sync` :c:func:`ProtSync()` does nothing in this implementation as +:c:func:`ProtSet()` sets the protection without any delay. + +:mps:tag:`fun.tramp` The protection trampoline, :c:func:`ProtTramp()`, is trivial +under Linux, as there is nothing that needs to be done in the dynamic +context of the mutator in order to catch faults. (Contrast this with +Win32 Structured Exception Handling.) + + +Threads +------- + +:mps:tag:`threads` The design must operate in a multi-threaded environment +(with LinuxThreads) and cooperate with the Linux support for locks +(see design.mps.lock) and the thread suspension mechanism (see +design.mps.pthreadext ). + +:mps:tag:`threads.suspend` The :c:macro:`SIGSEGV` signal handler does not mask out +any signals, so a thread may be suspended while the handler is active, +as required by the design (see +design.mps.pthreadext.req.suspend.protection). The signal handlers +simply nest at top of stack. + +:mps:tag:`threads.async` POSIX (and hence Linux) imposes some restrictions +on signal handler functions (see +design.mps.pthreadext.anal.signal.safety). Basically the rules say the +behaviour of almost all POSIX functions inside a signal handler is +undefined, except for a handful of functions which are known to be +"async-signal safe". However, if it's known that the signal didn't +happen inside a POSIX function, then it is safe to call arbitrary +POSIX functions inside a handler. + +:mps:tag:`threads.async.protection` If the signal handler is invoked because +of an MPS access, then we know the access must have been caused by +client code, because the client is not allowed to permit access to +protectable memory to arbitrary foreign code. In these circumstances, +it's OK to call arbitrary POSIX functions inside the handler. + +.. note:: + + Need a reference for "the client is not allowed to permit access + to protectable memory to arbitrary foreign code". + +:mps:tag:`threads.async.other` If the signal handler is invoked for some +other reason (that is, one we are not prepared to handle) then there +is less we can say about what might have caused the SEGV. In general +it is not safe to call arbitrary POSIX functions inside the handler in +this case. + +:mps:tag:`threads.async.choice` The signal handler calls :c:func:`ArenaAccess()` +to determine whether the segmentation fault was the result of an MPS +access. ArenaAccess will claim various MPS locks (that is, the arena +ring lock and some arena locks). The code calls no other POSIX +functions in the case where the segmentation fault is not an MPS +access. The locks are implemented as mutexes and are claimed by +calling :c:func:`pthread_mutex_lock()`, which is not defined to be +async-signal safe. + +:mps:tag:`threads.async.choice.ok` However, despite the fact that PThreads +documentation doesn't define the behaviour of :c:func:`pthread_mutex_lock()` +in these circumstances, we expect the LinuxThreads implementation will +be well-behaved unless the segmentation fault occurs while while in +the process of locking or unlocking one of the MPS locks (see +:mps:ref:`.threads.async.linux-mutex`). But we can assume that a segmentation +fault will not happen then (because we use the locks correctly, and +generally must assume that they work). Hence we conclude that it is OK +to call :c:func:`ArenaAccess()` directly from the signal handler. + +:mps:tag:`threads.async.linux-mutex` A study of the LinuxThreads source code +reveals that mutex lock and unlock functions are implemented as a +spinlock (using a locked compare-and-exchange instruction) with a +backup suspension mechanism using :c:func:`sigsuspend()`. On locking, the +spinlock code performs a loop which examines the state of the lock, +and then atomically tests that the state is unchanged while attempting +to modify it. This part of the code is reentrant (and hence +async-signal safe). Eventually, when locking, the spinlock code may +need to block, in which case it calls :c:func:`sigsuspend()`, waiting for +the manager thread to unblock it. The unlocking code is similar, +except that this code may need to release another thread, in which +case it calls :c:func:`kill()`. The functions :c:func:`sigsuspend()` and +:c:func:`kill()` are both defined to be async-signal safe by POSIX. In +summary, the mutex locking functions use primitives which are entirely +async-signal safe. They perform side-effects which modify the fields +of the lock structure only. This code may be safely invoked inside a +signal handler unless the interrupted function is in the process of +manipulating the fields of that lock structure. + +:mps:tag:`threads.async.improve` In future it would be preferable to not +have to assume reentrant mutex locking and unlocking functions. By +making the assumption we also assume that the implementation of +mutexes in LinuxThreads will not be completely re-designed in future +(which is not wise for the long term). An alternative approach would +be necessary anyway when supporting another platform which doesn't +offer reentrant locks (if such a platform does exist). + +:mps:tag:`threads.async.improve.how` We could avoid the assumption if we had +a means of testing whether an address lies within an arena chunk +without the need to claim any locks. Such a test might actually be +possible. For example, arenas could update a global datastructure +describing the ranges of all chunks, using atomic updates rather than +locks; the handler code would be allowed to read this without locking. +However, this is somewhat tricky; a particular consideration is that +it's not clear when it's safe to deallocate stale portions of the +datastructure. + +:mps:tag:`threads.sig-stack` We do not handle signals on a separate signal +stack. Separate signal stacks apparently don't work properly with +Pthreads. + + diff --git a/mps/manual/html/_sources/design/protocol.txt b/mps/manual/html/_sources/design/protocol.txt index 45b574e4301..74020429335 100644 --- a/mps/manual/html/_sources/design/protocol.txt +++ b/mps/manual/html/_sources/design/protocol.txt @@ -1,6 +1,517 @@ +.. _design-protocol: + + .. index:: pair: protocol inheritance; design -.. _design-protocol: -.. include:: ../../converted/protocol.rst +Protocol inheritance +==================== + +.. mps:prefix:: design.mps.protocol + + +Introduction +------------ + +:mps:tag:`intro` This document explains the design of the support for class +inheritance in MPS. It is not yet complete. It describes support for +single inheritance of classes. Future extensions will describe +multiple inheritance and the relationship between instances and +classes. + +:mps:tag:`readership` This document is intended for any MPS developer. + + +Purpose +------- + +:mps:tag:`purpose.code-maintain` The purpose of the protocol inheritance +design is to ensure that the MPS code base can make use of the +benefits of object-oriented class inheritance to maximize code reuse, +minimize code maintenance and minimize the use of boilerplate code. + +:mps:tag:`purpose.related` For related discussion, see +mail.tony.1998-08-28.16-26(0), mail.tony.1998-09-01.11-38(0), +mail.tony.1998-10-06.11-03(0) and other messages in the same threads. + + +Requirements +------------ + +:mps:tag:`req.implicit` The object system should provide a means for classes +to inherit the methods of their direct superclasses implicitly for all +functions in the protocol without having to write any explicit code +for each inherited function. + +:mps:tag:`req.override` There must additionally be a way for classes to +override the methods of their superclasses. + +:mps:tag:`req.next-method` As a result of :mps:ref:`.req.implicit`, classes cannot +make static assumptions about methods used by direct superclasses. The +object system must provide a means for classes to extend (not just +replace) the behaviour of protocol functions, such as a mechanism for +invoking the "next-method". + +:mps:tag:`req.ideal.extend` The object system must provide a standard way +for classes to implement the protocol supported by they superclass and +additionally add new methods of their own which can be specialized by +subclasses. + +:mps:tag:`req.ideal.multiple-inheritance` The object system should support +multiple inheritance such that sub-protocols can be "mixed in" with +several classes which do not themselves support identical protocols. + + +Overview +-------- + +.. c:type:: struct ProtocolClassStruct *ProtocolClass + +:mps:tag:`overview.root` We start with the root of all conformant class +hierarchies, which is called :c:type:`ProtocolClass`. This is an "abstract" +class (that is, it has no direct instances, but it is intended to have +subclasses). To use Dylan terminology, instances of its subclasses are +"general" instances of ProtocolClass. They look like this:: + + Instance Object Class Object + + -------------------- -------------------- + | sig | |-------->| sig | + -------------------- | -------------------- + | class |----| | superclass | + -------------------- -------------------- + | ... | | coerceInst | + -------------------- -------------------- + | ... | | coerceClass | + -------------------- -------------------- + | | | ... | + +:mps:tag:`overview.inherit` Classes inherit the protocols supported by their +superclasses. By default they have the same methods as the class(es) +from which they inherit. + +:mps:tag:`overview.inherit.specialize` Classes may specialize the behaviour +of their superclass. They do this by by overriding methods or other +fields in the class object. + +:mps:tag:`overview.extend` Classes may extend the protocols supported by +their superclasses by adding new fields for methods or other data. + +:mps:tag:`overview.sig.inherit` Classes will contain (possibly several) +signatures. Classes must not specialize (override) the signatures they +inherit from their superclasses. + +:mps:tag:`overview.sig.extend` If a class definition extends a protocol, it +is normal policy for the class definition to include a new signature +as the last field in the class object. + +:mps:tag:`overview.coerce-class` Each class contains a ``coerceClass`` +field. This contains a method which can find the part of the class +object which implements the protocols of a supplied superclass +argument (if, indeed, the argument *is* a superclass). This function +may be used for testing subclass/superclass relationships, and it also +provides support for multiple inheritance. + +:mps:tag:`overview.coerce-inst` Each class contains a ``coerceInst`` field. +This contains a method which can find the part of an instance object +which contains the instance slots of a supplied superclass argument +(if, indeed, the argument *is* a superclass). This function may be +used for testing whether an object is an instance of a given class, +and it also provides support for multiple inheritance. + +:mps:tag:`overview.superclass` Each class contains a ``superclass`` field. +This enables classes to call "next-method", as well as enabling the +coercion functions. + +:mps:tag:`overview.next-method` A specialized method in a class can make use +of an overridden method from a superclass by accessing the method from +the appropriate field in the superclass object and calling it. The +superclass may be accessed indirectly from the class's "Ensure" +function when it is statically known (see :mps:ref:`.overview.access`). This +permits "next-method" calls, and is fully scalable in that it allows +arbitrary length method chains. The :c:func:`SUPERCLASS()` macro helps with +this (see :mps:ref:`.int.static-superclass`). + +:mps:tag:`overview.next-method.naive` In some cases it is necessary to write +a method which is designed to specialize an inherited method, needs to +call the next-method, and yet the implementation doesn't have static +knowledge of the superclass. This might happen because the specialized +method is designed to be reusable by many class definitions. The +specialized method can usually locate the class object from one of the +parameters passed to the method. It can then access the superclass +through the ``superclass`` field of the class, and hence call the next +method. This technique has some limitations and doesn't support longer +method chains. It is also dependent on none of the class definitions +which use the method having any subclasses. + +:mps:tag:`overview.access` Classes must be initialized by calls to +functions, since it is these function calls which copy properties from +superclasses. Each class must provide an "Ensure" function, which +returns the canonical copy of the class. The canonical copy may reside +in static storage, but no MPS code may refer to that static storage by +name. + +:mps:tag:`overview.naming` There are some strict naming conventions which +must be followed when defining and using classes. The use is +obligatory because it is assumed by the macros which support the +definition and inheritance mechanism. For every class :c:type:`SomeClass`, +we insist upon the following naming conventions:- + +* :c:type:`SomeClassStruct` + + names the type of the structure for the protocol class. This might + be a ``typedef`` which aliases the type to the type of the + superclass, but if the class has extended the protocols of the + superclass the it will be a type which contains the new class + fields. + +* :c:type:`SomeClass` + + names the type ``*SomeClassStruct``. This might be a ``typedef`` + which aliases the type to the type of the superclass, but if the + class has extended the protocols of the superclass then it will be + a type which contains the new class fields. + +* :c:func:`EnsureSomeClass()` + + names the function that returns the initialized class object. + + + +Interface +--------- + +Class definition +................ + +.. c:function:: DEFINE_CLASS(className, var) + +:mps:tag:`int.define-class` Class definition is performed by the macro +:c:func:`DEFINE_CLASS()`. A call to the macro must be followed by a body of +initialization code in braces ``{}``. The parameter ``className`` is +used to name the class being defined. The parameter ``var`` is used to +name a local variable of type ``className``, which is defined by the +macro; it refers to the canonical storage for the class being defined. +This variable may be used in the initialization code. (The macro +doesn't just pick a name implicitly because of the danger of a name +clash with other names used by the programmer). A call to +``DEFINE_CLASS(SomeClass, var)`` defines the :c:func:`EnsureSomeClass()` +function, defines some static storage for the canonical class object, +and defines some other things to ensure the class gets initialized +exactly once. + +.. c:function:: DEFINE_ALIAS_CLASS(className, typeName, var) + +:mps:tag:`int.define-alias-class` A convenience macro +:c:func:`DEFINE_ALIAS_CLASS()` is provided which both performs the class +definition and defines the types :c:type:`SomeClass` and ``SomeClass +struct`` as aliases for some other class types. This is particularly +useful for classes which simply inherit, and don't extend protocols. +The macro call ``DEFINE_ALIAS_CLASS(className, superName, var)`` is +exactly equivalent to the following:: + + typedef superName className; + typedef superNameStruct classNameStruct; + DEFINE_CLASS(className, var) + +:mps:tag:`int.define-special` If classes are particularly likely to be +subclassed without extension, the class implementor may choose to +provide a convenience macro which expands into :c:func:`DEFINE_ALIAS_CLASS()` +with an appropriate name for the superclass. For example, there might +be a macro for defining pool classes such that the macro call +``DEFINE_POOL_CLASS(className, var)`` is exactly equivalent to the +macro call ``DEFINE_ALIAS_CLASS(className, PoolClass, var)``. It may +also be convenient to define a static superclass accessor macro at the +same time (see :mps:ref:`.int.static-superclass.special`). + + +Single inheritance +.................. + +.. c:function:: INHERIT_CLASS(thisClassCoerced, parentName) + +:mps:tag:`int.inheritance` Class inheritance details must be provided in the +class initialization code (see :mps:ref:`.int.define-class`). Inheritance is +performed by the macro :c:func:`INHERIT_CLASS()`. A call to this macro will +make the class being defined a direct subclass of ``parentClassName`` +by ensuring that all the fields of the parent class are copied into +``thisClass``, and setting the superclass field of ``thisClass`` to be +the parent class object. The parameter ``thisClassCoerced`` must be of +type ``parentClassName``. If the class definition defines an alias +class (see :mps:ref:`.int.define-alias-class`), then the variable named as the +second parameter to :c:func:`DEFINE_CLASS()` will be appropriate to pass to +:c:func:`INHERIT_CLASS()`. + + +Specialization +.............. + +:mps:tag:`int.specialize` Class specialization details must be given +explicitly in the class initialization code (see +:mps:ref:`.int.define-class`). This must happen *after* the inheritance +details are given (see :mps:ref:`.int.inheritance`). + + +Extension +......... + +:mps:tag:`int.extend` To extend the protocol when defining a new class, a +new type must be defined for the class structure. This must embed the +structure for the primarily inherited class as the first field of the +structure. Class extension details must be given explicitly in the +class initialization code (see :mps:ref:`.int.define-class`). This must happen +*after* the inheritance details are given (see :mps:ref:`.int.inheritance`). + + +Introspection +............. + +:mps:tag:`introspect.c-lang` The design includes a number of introspection +functions for dynamically examining class relationships. These +functions are polymorphic and accept arbitrary subclasses of +:c:type:`ProtocolClass`. C doesn't support such polymorphism. So although +these have the semantics of functions (and could be implemented as +functions in another language with compatible calling conventions) +they are actually implemented as macros. The macros are named as +method-style macros despite the fact that this arguably contravenes +guide.impl.c.macro.method. The justification for this is that this +design is intended to promote the use of polymorphism, and it breaks +the abstraction for the users to need to be aware of what can and +can't be expressed directly in C function syntax. These functions all +have names ending in ``Poly`` to identify them as polymorphic +functions. + +.. c:function:: ProtocolClassSuperclassPoly(class) + +:mps:tag:`int.superclass` An introspection function which returns the direct +superclass of class object ``class``. + +.. c:function:: SUPERCLASS(className) + +:mps:tag:`int.static-superclass` An introspection macro which returns the +direct superclass given a class name, which must (obviously) be +statically known. The macro expands into a call to the ensure function +for the class name, so this must be in scope (which may require a +forward declaration). The macro is useful for next-method calls (see +:mps:ref:`.overview.next-method`). The superclass is returned with type +:c:type:`ProtocolClass` so it may be necessary to cast it to the type for +the appropriate subclass. + +:mps:tag:`int.static-superclass.special` Implementors of classes which are +designed to be subclassed without extension may choose to provide a +convenience macro which expands into a call to :c:macro:`SUPERCLASS` along +with a type cast. For example, there might be a macro for finding pool +superclasses such that the macro call ``POOL_SUPERCLASS(className)`` +is exactly equivalent to ``(PoolClass)SUPERCLASS(className)``. It's +convenient to define these macros alongside the convenience class +definition macro (see :mps:ref:`.int.define-special`). + +.. c:function:: ClassOfPoly(inst) + +:mps:tag:`int.class` An introspection function which returns the class of +which ``inst`` is a direct instance. + +.. c:function:: IsSubclassPoly(sub, super) + +:mps:tag:`int.subclass` An introspection function which returns a :c:type:`Bool` +indicating whether ``sub`` is a subclass of ``super``. That is, it is +a predicate for testing subclass relationships. + + +Multiple inheritance +.................... + +:mps:tag:`int.mult-inherit` Multiple inheritance involves an extension of +the protocol (see :mps:ref:`.int.extend`) and also multiple uses of the single +inheritance mechanism (see :mps:ref:`.int.inheritance`). It also requires +specialized methods for ``coerceClass`` and ``coerceInst`` to be +written (see :mps:ref:`.overview.coerce-class` and :mps:ref:`.overview.coerce-inst`). +Documentation on support for multiple inheritance is under +construction. This facility is not currently used. The basic idea is +described in mail.tony.1998-10-06.11-03(0). + + +Protocol guidelines +................... + +:mps:tag:`guide.fail` When designing an extensible function which might +fail, the design must permit the correct implementation of the +failure-case code. Typically, a failure might occur in any method in +the chain. Each method is responsible for correctly propagating +failure information supplied by superclass methods and for managing +it's own failures. + +:mps:tag:`guide.fail.before-next` Dealing with a failure which is detected +before any next-method call is made is similar to a fail case in any +non-extensible function. See :mps:ref:`.example.fail` below. + +:mps:tag:`guide.fail.during-next` Dealing with a failure returned from a +next-method call is also similar to a fail case in any non-extensible +function. See :mps:ref:`.example.fail` below. + +:mps:tag:`guide.fail.after-next` Dealing with a failure which is detected +after the next methods have been successfully invoked is more complex. +If this scenario is possible, the design must include an +"anti-function", and each class must ensure that it provides a method +for the anti-method which will clean up any resources which are +claimed after a successful invocation of the main method for that +class. Typically the anti-function would exist anyway for clients of +the protocol (for example, "finish" is an anti-function for "init"). +The effect of the next-method call can then be cleaned up by calling +the anti-method for the superclass. See :mps:ref:`.example.fail` below. + + +Example +....... + +:mps:tag:`example.inheritance` The following example class definition shows +both inheritance and specialization. It shows the definition of the +class :c:type:`EPDRPoolClass`, which inherits from :c:type:`EPDLPoolClass` and has +specialized values of the ``name``, ``init``, and ``alloc`` fields. +The type :c:type:`EPDLPoolClass` is an alias for :c:type:`PoolClass`. :: + + typedef EPDLPoolClass EPDRPoolClass; + typedef EPDLPoolClassStruct EPDRPoolClassStruct; + + DEFINE_CLASS(EPDRPoolClass, this) + { + INHERIT_CLASS(this, EPDLPoolClass); + this->name = "EPDR"; + this->init = EPDRInit; + this->alloc = EPDRAlloc; + } + +:mps:tag:`example.extension` The following (hypothetical) example class +definition shows inheritance, specialization and also extension. It +shows the definition of the class :c:type:`EPDLDebugPoolClass`, which +inherits from :c:type:`EPDLPoolClass`, but also implements a method for +checking properties of the pool. :: + + typedef struct EPDLDebugPoolClassStruct { + EPDLPoolClassStruct epdl; + DebugPoolCheckMethod check; + Sig sig; + } EPDLDebugPoolClassStruct; + + typedef EPDLDebugPoolClassStruct *EPDLDebugPoolClass; + + DEFINE_CLASS(EPDLDebugPoolClass, this) + { + EPDLPoolClass epdl = &this->epdl; + INHERIT_CLASS(epdl, EPDLPoolClass); + epdl->name = "EPDLDBG"; + this->check = EPDLDebugCheck; + this->sig = EPDLDebugSig; + } + +:mps:tag:`example.fail` The following example shows the implementation of +failure-case code for an "init" method, making use of the "finish" +anti-method:: + + static Res mySegInit(Seg seg, Pool pool, Addr base, Size size, + Bool reservoirPermit, va_list args) + { + SegClass super; + MYSeg myseg; + OBJ1 obj1; + Res res; + Arena arena; + + AVERT(Seg, seg); + myseg = SegMYSeg(seg); + AVERT(Pool, pool); + arena = PoolArena(pool); + + /* Ensure the pool is ready for the segment */ + res = myNoteSeg(pool, seg); + if(res != ResOK) + goto failNoteSeg; + + /* Initialize the superclass fields first via next-method call */ + super = (SegClass)SUPERCLASS(MYSegClass); + res = super->init(seg, pool, base, size, reservoirPermit, args); + if(res != ResOK) + goto failNextMethods; + + /* Create an object after the next-method call */ + res = ControlAlloc(&obj1, arena, sizeof(OBJ1Struct), reservoirPermit); + if(res != ResOK) + goto failObj1; + + myseg->obj1 = obj1 + return ResOK; + + failObj1: + /* call the anti-method for the superclass */ + super->finish(seg); + failNextMethods: + /* reverse the effect of myNoteSeg */ + myUnnoteSeg(pool, seg); + failNoteSeg: + return res; + } + + +Implementation +-------------- + +:mps:tag:`impl.derived-names` The :c:func:`DEFINE_CLASS()` macro derives some +additional names from the class name as part of it's implementation. +These should not appear in the source code - but it may be useful to +know about this for debugging purposes. For each class definition for +class :c:type:`SomeClass`, the macro defines the following: + +* ``extern SomeClass EnsureSomeClass(void);`` + + The class accessor function. See :mps:ref:`.overview.naming`. + +* ``static Bool protocolSomeClassGuardian;`` + + A Boolean which indicates whether the class has been initialized + yet. + +* ``static void protocolEnsureSomeClass(SomeClass);`` + + A function called by :c:func:`EnsureSomeClass()`. All the class + initialization code is actually in this function. + +* ``static SomeClassStruct protocolSomeClassStruct;`` + + Static storage for the canonical class object. + +:mps:tag:`impl.init-once` Class objects only behave according to their +definition after they have been initialized, and class protocols may +not be used before initialization has happened. The only code which is +allowed to see a class object in a partially initialized state is the +initialization code itself -- and this must take care not to pass the +object to any other code which might assume it is initialized. Once a +class has been initialized, the class might have a client. The class +must not be initialized again when this has happened, because the +state is not necessarily consistent in the middle of an initialization +function. The initialization state for each class is stored in a +Boolean "guardian" variable whose name is derived from the class name +(see :mps:ref:`.impl.derived-names`). This ensures the initialization happens +only once. The path through the :c:func:`EnsureSomeClass()` function should be +very fast for the common case when this variable is :c:macro:`TRUE`, and the +class has already been initialized, as the canonical static storage +can simply be returned in that case. However, when the value of the +guardian is :c:macro:`FALSE`, the class is not initialized. In this case, a +call to :c:func:`EnsureSomeClass()` must first execute the initialization code +and then set the guardian to :c:macro:`TRUE`. However, this must happen +atomically (see :mps:ref:`.impl.init-lock`). + +:mps:tag:`impl.init-lock` There would be the possibility of a race condition +if :c:func:`EnsureSomeClass()` were called concurrently on separate threads +before :c:type:`SomeClass` has been initialized. The class must not be +initialized more than once, so the sequence test-guard, init-class, +set-guard must be run as a critical region. It's not sufficient to use +the arena lock to protect the critical region, because the class +object might be shared between multiple arenas. The :c:func:`DEFINE_CLASS()` +macro uses a global recursive lock instead. The lock is only claimed +after an initial unlocked access of the guard variable shows that the +class is not initialized. This avoids any locking overhead for the +common case where the class is already initialized. This lock is +provided by the lock module -- see design.mps.lock(0). + + diff --git a/mps/manual/html/_sources/design/protsu.txt b/mps/manual/html/_sources/design/protsu.txt new file mode 100644 index 00000000000..530e446ae76 --- /dev/null +++ b/mps/manual/html/_sources/design/protsu.txt @@ -0,0 +1,118 @@ +.. _design-protsu: + + +.. index:: + pair: SunOS 4; protection interface design + pair: SunOS 4 protection interface; design + + +SunOS 4 protection module +========================= + +.. mps:prefix:: design.mps.protsu + pair: SunOS 4; protection interface design + pair: SunOS 4 protection interface; design + + +.. warning:: + + As of 2013-05-26, the MPS is no longer supported on SunOS, so + this document is only of historical interest. + + +Introduction +------------ + +:mps:tag:`readership` Any MPS developer. + +:mps:tag:`intro` This is the design of the SunOS 4 implementation of the +protection module. It is intended to be used only in SunOS 4 (os.su). +It makes use of various services provided by SunOS 4. + + +Requirements +------------ + +:mps:tag:`req.general` Required to implement the general protection +interface defined in design.mps.prot.if.*. + + +Overview +-------- + +Uses :c:func:`mprotect()`. + + +Misc +---- + +:mps:tag:`improve.sig-stack` Currently we do not handle signals on a +separate signal stack. If we handled signals on our own stack then we +could guarantee not to run out of stack while we were handling the +signal. This would be useful (it may even be required). We would have +to use ``sigvec(2)`` rather than ``signal(3)`` (set the :c:macro:`SV_ONSTACK` +flag and use ``sigstack(2)``). This has drawbacks as the signal stack +is not grown automatically, so we would have to to frig the stacks +back if we wanted to pass on the signal to some other handler as that +handler may require arbitrary amounts of stack. + +:mps:tag:`improve.sigvec` Note 1 of :c:func:`ProtSetup()` notes that we can't +honour the ``sigvec(2)`` entries of the next handler in the chain. +What if when we want to pass on the signal instead of calling the +handler we call :c:func:`sigvec()` with the old entry and use kill to send +the signal to ourselves and then restore our handler using sigvec +again. + + +Data structures +--------------- + +:mps:tag:`data.signext` This is static. Because that is the only +communications channel available to signal handlers. [write a little +more here] + + +Functions +--------- + +:mps:tag:`fun.setup` :c:func:`ProtSetup()`. The setup involves installing a signal +handler for the signal :c:macro:`SIGSEGV` to catch and handle protection +faults (this handler is the function :c:func:`sigHandle()`, see +:mps:ref:`.fun.sighandle`). The previous handler is recorded (in the variable +``sigNext``, see :mps:ref:`.data.signext`) so that it can be reached from +:c:func:`sigHandle()` if it fails to handle the fault. + +The problem with this approach is that we can't honor the wishes of the +``sigvec(2)`` entry for the previous handler (in terms of masks in particular). + +Obviously it would be okay to always chain the previous signal handler +onto ``sigNext``, however in the case where the previous handler is +the one we've just installed (that is, ``sigHandle``) then it is not +necessary to chain the handler, so we don't. + +:mps:tag:`fun.set` :c:func:`ProtSet()` + +:mps:tag:`fun.set.convert` The requested protection (which is expressed in +the mode parameter, see design.mps.prot.if.set) is translated into an +operating system protection. If read accesses are to be forbidden then +all accesses are forbidden, this is done by setting the protection of +the page to :c:macro:`PROT_NONE`. If write access are to be forbidden (and +not read accesses) then write accesses are forbidden and read accesses +are allowed, this is done by setting the protection of the page to +``PROT_READ | PROT_EXEC``. Otherwise (all access are okay), the +protection is set to ``PROT_READ | PROT_WRITE | PROT_EXEC``. + +:mps:tag:`fun.set.assume.mprotect` We assume that the call to :c:func:`mprotect()` +always succeeds. This is because we should always call the function +with valid arguments (aligned, references to mapped pages, and with an +access that is compatible with the access of the underlying object). + +:mps:tag:`fun.sync` :c:func:`ProtSync()`. This does nothing in this implementation +as ProtSet sets the protection without any delay. + +:mps:tag:`fun.tramp` :c:func:`ProtTramp()`. The protection trampoline is trivial +under SunOS, as there is nothing that needs to be done in the dynamic +context of the mutator in order to catch faults. (Contrast this with +Win32 Structured Exception Handling.) + + diff --git a/mps/manual/html/_sources/design/pthreadext.txt b/mps/manual/html/_sources/design/pthreadext.txt new file mode 100644 index 00000000000..90ed4a1a3b6 --- /dev/null +++ b/mps/manual/html/_sources/design/pthreadext.txt @@ -0,0 +1,344 @@ +.. _design-pthreadext: + + +.. index:: + pair: POSIX thread extensions; design + + +POSIX thread extensions +======================= + +.. mps:prefix:: design.mps.pthreadext + + +Introduction +------------ + +:mps:tag:`readership` Any MPS developer. + +:mps:tag:`intro` This is the design of the Pthreads extension module, which +provides some low-level threads support for use by MPS (notably +suspend and resume). + + +Definitions +----------- + +:mps:tag:`pthreads` The term "Pthreads" means an implementation of the POSIX +1003.1c-1995 thread standard. (Or the Single UNIX Specification, +Version 2, aka USV2 or UNIX98.) + +:mps:tag:`context` The "context" of a thread is a (platform-specific) +OS-defined structure which describes the current state of the +registers for that thread. + + +Requirements +------------ + +:mps:tag:`req.suspend` A means to suspend threads, so that they don't make +any progress. + +:mps:tag:`req.suspend.why` Needed by the thread manager so that other +threads registered with an arena can be suspended (see +design.mps.thread-manager). Not directly provided by Pthreads. + +:mps:tag:`req.resume` A means to resume suspended threads, so that they are +able to make progress again. :mps:tag:`req.resume.why` Needed by the thread +manager. Not directly provided by Pthreads. + +:mps:tag:`req.suspend.multiple` Allow a thread to be suspended on behalf of +one arena when it has already been suspended on behalf of one or more +other arenas. :mps:tag:`req.suspend.multiple.why` The thread manager +contains no design for cooperation between arenas to prevent this. + +:mps:tag:`req.resume.multiple` Allow requests to resume a thread on behalf +of each arena which had previously suspended the thread. The thread +must only be resumed when requests from all such arenas have been +received. :mps:tag:`req.resume.multiple.why` A thread manager for an arena +must not permit a thread to make progress before it explicitly resumes +the thread. + +:mps:tag:`req.suspend.context` Must be able to access the context for a +thread when it is suspended. + +:mps:tag:`req.suspend.protection` Must be able to suspend a thread which is +currently handling a protection fault (i.e., an arena access). Such a +thread might even own an arena lock. + +:mps:tag:`req.legal` Must use the Pthreads and other POSIX APIs in a legal +manner. + + + +Analysis +-------- + +:mps:tag:`anal.suspend` Thread suspension is inherently asynchronous. MPS +needs to be able to suspend another thread without prior knowledge of +the code that thread is running. (That is, we can't rely on +cooperation between threads.) The only asynchronous communication +available on POSIX is via signals -- so the suspend and resume +mechanism must ultimately be built from signals. + +:mps:tag:`anal.signal.safety` POSIX imposes some restrictions on what a +signal handler function might do when invoked asynchronously (see the +sigaction_ documentation, and search for the string "reentrant"). In +summary, a small number of POSIX functions are defined to be +"async-signal safe", which means they may be invoked without +restriction in signal handlers. All other POSIX functions are +considered to be unsafe. Behaviour is undefined if an unsafe function +is interrupted by a signal and the signal handler then proceeds to +call another unsafe function. See mail.tony.1999-08-24.15-40(0)and +followups for some further analysis. + +.. _sigaction: http://www.opengroup.org/onlinepubs/007908799/xsh/sigaction.html + +:mps:tag:`anal.signal.safety.implication` Since we can't assume that we +won't attempt to suspend a thread while it is running an unsafe +function, we must limit the use of POSIX functions in the suspend +signal handler to those which are designed to be "async-signal safe". +One of the few such functions related to synchronization is +:c:func:`sem_post()`. + +:mps:tag:`anal.signal.example` An example of how to suspend threads in POSIX +was posted to newsgroup comp.programming.threads in August 1999 +[Lau_1999-08-16]_. The code in the post was written by David Butenhof, who +contributed some comments on his implementation [Butenhof_1999-08-16]_ + +:mps:tag:`anal.signal.linux-hack` In the current implementation of Linux +Pthreads, it would be possible to implement suspend/resume using +:c:macro:`SIGSTOP` and :c:macro:`SIGCONT`. This is, however, nonportable and will +probably stop working on Linux at some point. + +:mps:tag:`anal.component` There is no known way to meet the requirements +above in a way which cooperates with another component in the system +which also provides its own mechanism to suspend and resume threads. +The best bet for achieving this is to provide the functionality in +shared low-level component which may be used by MPS and other clients. +This will require some discussion with other potential clients and/or +standards bodies. + +:mps:tag:`anal.component.dylan` Note that such cooperation is actually a +requirement for Dylan (req.dylan.dc.env.self), though this is not a +problem, since all the Dylan components share the MPS mechanism. + + +Interface +--------- + +.. c:type:: PThreadextStruct *PThreadext + +:mps:tag:`if.pthreadext.abstract` A thread is represented by the abstract +type :c:type:`PThreadext`. A :c:type:`PThreadext` object corresponds directly with +a PThread (of type ``pthread_t``). There may be more than one +:c:type:`PThreadext` object for the same PThread. + +:mps:tag:`if.pthreadext.structure` The structure definition of +:c:type:`PThreadext` (:c:type:`PThreadextStruct`) is exposed by the interface so +that it may be embedded in a client datastructure (for example, +:c:type:`ThreadStruct`). This means that all storage management can be left +to the client (which is important because there might be multiple +arenas involved). Clients may not access the fields of a +:c:type:`PThreadextStruct` directly. + +.. c:function:: void PThreadextInit(PThreadext pthreadext, pthread_t id) + +:mps:tag:`if.init` Initializes a :c:type:`PThreadext` object for a thread with the +given ``id``. + +.. c:function:: Bool PThreadextCheck(PThreadext pthreadext) + +:mps:tag:`if.check` Checks a :c:type:`PThreadext` object for consistency. Note +that this function takes the mutex, so it must not be called with the +mutex held (doing so will probably deadlock the thread). + +.. c:function:: Res PThreadextSuspend(PThreadext pthreadext, struct sigcontext **contextReturn) + +:mps:tag:`if.suspend` Suspends a :c:type:`PThreadext` object (puts it into a +suspended state). Meets :mps:ref:`.req.suspend`. The object must not already +be in a suspended state. If the function returns ``ResOK``, the +context of the thread is returned in contextReturn, and the +corresponding PThread will not make any progress until it is resumed: + +.. c:function:: Res PThreadextResume(PThreadext pthreadext) + +:mps:tag:`if.resume` Resumes a :c:type:`PThreadext` object. Meets +:mps:ref:`.req.resume`. The object must already be in a suspended state. +Puts the object into a non-suspended state. Permits the corresponding +PThread to make progress again, (although that might not happen +immediately if there is another suspended :c:type:`PThreadext` object +corresponding to the same thread): + +.. c:function:: void PThreadextFinish(PThreadext pthreadext) + +:mps:tag:`if.finish` Finishes a PThreadext object. + + + +Implementation +-------------- + +.. c:type:: struct PThreadextStruct PThreadextStruct + +:mps:tag:`impl.pthreadext` The structure definition for a :c:type:`PThreadext` +object is:: + + struct PThreadextStruct { + Sig sig; /* design.mps.sig */ + pthread_t id; /* Thread ID */ + struct sigcontext *suspendedScp; /* sigcontext if suspended */ + RingStruct threadRing; /* ring of suspended threads */ + RingStruct idRing; /* duplicate suspensions for id */ + }; + +:mps:tag:`impl.field.id` The ``id`` field shows which PThread the object +corresponds to. + +:mps:tag:`impl.field.scp` The ``suspendedScp`` field contains the context +when in a suspended state. Otherwise it is :c:macro:`NULL`. + +:mps:tag:`impl.field.threadring` The ``threadRing`` field is used to chain +the object onto the suspend ring when it is in the suspended state +(see :mps:ref:`.impl.suspend-ring`). When not in a suspended state, this ring +is single. + +:mps:tag:`impl.field.idring` The ``idRing`` field is used to group the +object with other objects corresponding to the same PThread (same +``id`` field) when they are in the suspended state. When not in a +suspended state, or when this is the only :c:type:`PThreadext` object with +this ``id`` in the suspended state, this ring is single. + +:mps:tag:`impl.global.suspend-ring` The module maintains a global +suspend-ring -- a ring of :c:type:`PThreadext` objects which are in a +suspended state. This is primarily so that it's possible to determine +whether a thread is curently suspended anyway because of another +:c:type:`PThreadext` object, when a suspend attempt is made. + +:mps:tag:`impl.global.victim` The module maintains a global variable which +is used to indicate which :c:type:`PThreadext` is the current victim during +suspend operations. This is used to communicate information between +the controlling thread and the thread being suspended (the victim). +The variable has value :c:macro:`NULL` at other times. + +:mps:tag:`impl.static.mutex` We use a lock (mutex) around the suspend and +resume operations. This protects the state data (the suspend-ring the +victim: see :mps:ref:`.impl.global.suspend-ring` and :mps:ref:`.impl.global.victim` +respectively). Since only one thread can be suspended at a time, +there's no possibility of two arenas suspending each other by +concurrently suspending each other's threads. + +:mps:tag:`impl.static.semaphore` We use a semaphore to synchronize between +the controlling and victim threads during the suspend operation. See +:mps:ref:`.impl.suspend` and :mps:ref:`.impl.suspend-handler`). + +:mps:tag:`impl.static.init` The static data and global variables of the +module are initialized on the first call to :c:func:`PThreadextSuspend()`, +using :c:func:`pthread_once()` to avoid concurrency problems. We also enable +the signal handlers at the same time (see :mps:ref:`.impl.suspend-handler` and +:mps:ref:`.impl.resume-handler`). + +:mps:tag:`impl.suspend` :c:func:`PThreadextSuspend()` first ensures the module is +initialized (see :mps:ref:`.impl.static.init`). After this, it claims the +mutex (see :mps:ref:`.impl.static.mutex`). It then checks to see whether +thread of the target :c:type:`PThreadext` object has already been suspended +on behalf of another :c:type:`PThreadext` object. It does this by iterating +over the suspend ring. + +:mps:tag:`impl.suspend.already-suspended` If another object with the same id +is found on the suspend ring, then the thread is already suspended. +The context of the target object is updated from the other object, and +the other object is linked into the ``idRing`` of the target. + +:mps:tag:`impl.suspend.not-suspended` If the thread is not already +suspended, then we forcibly suspend it using a technique similar to +Butenhof's (see :mps:ref:`.anal.signal.example`): First we set the victim +variable (see :mps:ref:`.impl.global.victim`) to indicate the target object. +Then we send the signal :c:macro:`PTHREADEXT_SIGSUSPEND` to the thread (see +:mps:ref:`.impl.signals`), and wait on the semaphore for it to indicate that +it has received the signal and updated the victim variable with the +context. If either of these operations fail (for example, because of +thread termination) we unlock the mutex and return ``ResFAIL``. + +:mps:tag:`impl.suspend.update` Once we have ensured that the thread is +definitely suspended, we add the target :c:type:`PThreadext` object to the +suspend ring, unlock the mutex, and return the context to the caller. + +:mps:tag:`impl.suspend-handler` The suspend signal handler is invoked in the +target thread during a suspend operation, when a +:c:macro:`PTHREADEXT_SIGSUSPEND` signal is sent by the controlling thread +(see :mps:ref:`.impl.suspend.not-suspended`). The handler determines the +context (received as a parameter, although this may be +platform-specific) and stores this in the victim object (see +:mps:ref:`.impl.global.victim`). The handler then masks out all signals except +the one that will be received on a resume operation +(:c:macro:`PTHREADEXT_SIGRESUME`) and synchronizes with the controlling +thread by posting the semaphore. Finally the handler suspends until +the resume signal is received, using :c:func:`sigsuspend()`. + +:mps:tag:`impl.resume` :c:func:`PThreadextResume()` first claims the mutex (see +:mps:ref:`.impl.static.mutex`). It then checks to see whether thread of the +target :c:type:`PThreadext` object has also been suspended on behalf of +another :c:type:`PThreadext` object (in which case the id ring of the target +object will not be single). + +:mps:tag:`impl.resume.also-suspended` If the thread is also suspended on +behalf of another :c:type:`PThreadext`, then the target object is removed from +the id ring. + +:mps:tag:`impl.resume.not-also` If the thread is not also suspended on +behalf of another :c:type:`PThreadext`, then the thread is resumed using the +technique proposed by Butenhof (see :mps:ref:`.anal.signal.example`). I.e. we +send it the signal :c:macro:`PTHREADEXT_SIGRESUME` (see :mps:ref:`.impl.signals`) and +expect it to wake up. If this operation fails (for example, because of +thread termination) we unlock the mutex and return ``ResFAIL``. + +:mps:tag:`impl.resume.update` Once the target thread is in the appropriate +state, we remove the target :c:type:`PThreadext` object from the suspend +ring, set its context to :c:macro:`NULL` and unlock the mutex. + +:mps:tag:`impl.resume-handler` The resume signal handler is invoked in the +target thread during a resume operation, when a +:c:macro:`PTHREADEXT_SIGRESUME` signal is sent by the controlling thread (see +:mps:ref:`.impl.resume.not-also`). The resume signal handler simply returns. +This is sufficient to unblock the suspend handler, which will have +been blocking the thread at the time of the signal. The Pthreads +implementation ensures that the signal mask is restored to the value +it had before the signal handler was invoked. + +:mps:tag:`impl.finish` :c:func:`PThreadextFinish()` supports the finishing of +objects in the suspended state, and removes them from the suspend ring +and id ring as necessary. It must claim the mutex for the removal +operation (to ensure atomicity of the operation). Finishing of +suspended objects is supported so that clients can dispose of +resources if a resume operation fails (which probably means that the +PThread has terminated). + +:mps:tag:`impl.signals` The choice of which signals to use for suspend and +restore operations may need to be platform-specific. Some signals are +likely to be generated and/or handled by other parts of the +application and so should not be used (for example, :c:macro:`SIGSEGV`). Some +implementations of PThreads use some signals for themselves, so they +may not be used; for example, LinuxThreads uses :c:macro:`SIGUSR1` and +:c:macro:`SIGUSR2` for its own purposes. The design abstractly names the +signals :c:macro:`PTHREADEXT_SIGSUSPEND` and :c:macro:`PTHREAD_SIGRESUME`, so that +they may be easily mapped to appropriate real signal values. Candidate +choices are :c:macro:`SIGXFSZ` and :c:macro:`SIGPWR`. + + +Attachments +----------- + +[missing attachment "posix.txt"] + +[missing attachment "susp.c"] + + +References +---------- + +.. [Butenhof_1999-08-16] Dave Butenhof. comp.programming.threads. 1999-08-16. "`Re: Problem with Suspend & Resume Thread Example `__". + +.. [Lau_1999-08-16] Raymond Lau. comp.programming.threads. 1999-08-16. "`Problem with Suspend & Resume Thread Example `__". + + diff --git a/mps/manual/html/_sources/design/reservoir.txt b/mps/manual/html/_sources/design/reservoir.txt index e954bdc62ab..0bc6dcce290 100644 --- a/mps/manual/html/_sources/design/reservoir.txt +++ b/mps/manual/html/_sources/design/reservoir.txt @@ -1,6 +1,115 @@ +.. _design-reservoir: + + .. index:: pair: reservoir; design -.. _design-reservoir: -.. include:: ../../converted/reservoir.rst +The low-memory reservoir +======================== + +.. mps:prefix:: design.mps.reservoir + + +Introduction +------------ + +:mps:tag:`intro` The low-memory reservoir provides client support for +implementing handlers for low-memory situations which allocate. The +reservoir is implemented inside the arena as a pool of unallocatable +segments. + + +Architecture +------------ + +.. c:type:: struct ReservoirStruct *Reservoir + +:mps:tag:`adt` The reservoir interface looks (almost) like an abstract data +type of type :c:type:`Reservoir`. It's not quite abstract because the arena +embeds the structure of the reservoir (of type :c:type:`ReservoirStruct`) +into its own structure, for simplicity of initialization. + +:mps:tag:`align` The reservoir is implemented as a pool of available tracts, +along with a size and limit which must always be aligned to the arena +alignment. The size corresponds to the amount of memory currently +maintained in the reservoir. The limit is the maximum amount that it +is desired to maintain. + +:mps:tag:`wastage` When the reservoir limit is set by the client, the actual +limit should be increased by an arena alignment amount for every +active mutator buffer. + +:mps:tag:`really-empty` When the reservoir limit is set to 0, assume that +the client really doesn't have a need for a reservoir at all. In this +case, the client won't even want an allowance to be made for wastage +in active buffers. + + +Implementation +-------------- + +:mps:tag:`interface` The following functions comprise the interface to the +reservoir module: + +.. c:function:: Bool ReservoirCheck(Reservoir reservoir) + +:mps:tag:`interface.check` :c:func:`ReservoirCheck()` checks the reservoir for +consistency. + +.. c:function:: Res ReservoirInit(Reservoir reservoir, Arena arena) + +:mps:tag:`interface.init` :c:func:`ReservoirInit()` initializes the reservoir and +its associated pool, setting the size and limit to 0. + +.. c:function:: void ReservoirFinish (Reservoir reservoir) + +:mps:tag:`interface.finish` :c:func:`ReservoirFinish()` de-initializes the reservoir +and its associated pool: + +.. c:function:: Size ReservoirLimit(Reservoir reservoir) + +:mps:tag:`interface.limit` :c:func:`ReservoirLimit()` returns the limit of the +reservoir: + +.. c:function:: void ReservoirSetLimit(Reservoir reservoir, Size size) + +:mps:tag:`interface.set-limit` :c:func:`ReservoirSetLimit()` sets the limit of the +reservoir, making an allowance for wastage in mutator buffers: + +.. c:function:: Size ReservoirAvailable(Reservoir reservoir) + +:mps:tag:`interface.available` :c:func:`ReservoirAvailable()` returns the available +size of the reservoir: + +.. c:function:: Res ReservoirEnsureFull(Reservoir reservoir) + +:mps:tag:`interface.ensure-full` :c:func:`ReservoirEnsureFull()` attempts to fill +the reservoir with memory from the arena, until it is full: + +.. c:function:: void ReservoirDeposit(Reservoir reservoir, Addr base, Size size) + +:mps:tag:`interface.deposit` :c:func:`ReservoirDeposit()` attempts to fill the +reservoir with memory in the supplied range, until it is full. This is +called by the arena from :c:func:`ArenaFree()` if the reservoir is not known +to be full. Any memory which is not added to the reservoir (because +the reservoir is full) is freed via the arena class's free method. + +.. c:function:: Res ReservoirWithdraw(Addr *baseReturn, Tract *baseTractReturn, Reservoir reservoir, Size size, Pool pool) + +:mps:tag:`interface.withdraw` :c:func:`ReservoirWithdraw()` attempts to allocate +memory of the specified size to the specified pool to the reservoir. +If no suitable memory can be found it returns ``ResMEMORY``. + +:mps:tag:`interface.withdraw.align` Currently, :c:func:`ReservoirWithdraw()` can +only withdraw memory in chunks of the size of the arena alignment. +This is because the reservoir doesn't attempt to coalesce adjacent +memory blocks. This deficiency should be fixed in the future. + +:mps:tag:`pool` The memory managed by the reservoir is owned by the +reservoir pool. This memory is never sub-allocated. Each tract +belonging to the pool is linked onto a list. The head of the list is +in the :c:type:`Reservoir` object. Links are stored in the ``TractP`` fields +of each tract object. + + diff --git a/mps/manual/html/_sources/design/ring.txt b/mps/manual/html/_sources/design/ring.txt index 09d1afbb04e..501e3d52c82 100644 --- a/mps/manual/html/_sources/design/ring.txt +++ b/mps/manual/html/_sources/design/ring.txt @@ -1,6 +1,193 @@ +.. _design-ring: + + .. index:: pair: ring structure; design -.. _design-ring: -.. include:: ../../converted/ring.rst +Ring data structure +=================== + +.. mps:prefix:: design.mps.ring + + +Introduction +------------ + +:mps:tag:`source` rings are derived from the earlier use of Deques. See +design.mps.deque. + + +Description +----------- + +.. c:type:: RingStruct *Ring + +:mps:tag:`def.ring` Rings are circular doubly-linked lists of ring "nodes". +The nodes are fields of structures which are the "elements" of the +ring. + +Ring node structures (:c:type:`RingStruct`) are inlined in the structures on +the ring, like this:: + + typedef struct FooStruct *Foo; /* the element type */ + typedef struct FooStruct { /* the element structure */ + int baz, bim; + RingStruct ring; /* the ring node */ + float bip, bop; + } FooStruct; + +This arrangement means that they do not need to be managed separately. +This is especially useful in avoiding re-entrancy and bootstrapping +problems in the memory manager. Rings also provide flexible insertion +and deletion because the entire ring can be found from any node. + +In the MPS, rings are used to connect a "parent" structure (such as a +:c:type:`Arena`) to a number of "child" structures (such as :c:type:`Pool`), as +shown in :mps:ref:`.fig.ring`. Note the slight abuse of naming convention, in +that ``barRing`` is not called ``barRingStruct``. + +:mps:tag:`fig.ring` A ring of ``Bar`` objects owned by a ``Foo`` object. + +[missing figure] + +:mps:tag:`fig.empty` An empty ring of ``Bar`` objects owned by a ``Foo`` +object. + +[missing figure] + +:mps:tag:`def.singleton` A "singleton" ring is a ring containing one node, +whose previous and next nodes are itself (see :mps:ref:`.fig.single`). + +:mps:tag:`fig.single` A singleton ``Bar`` object not on any ring. + +[missing figure] + +:mps:tag:`fig.elt` How :c:func:`RING_ELT()` gets a parent pointer from a node pointer. + +[missing figure] + + +Init / Finish +------------- + +.. c:function:: void RingInit(Ring ring) + +:mps:tag:`init` Rings are initialized with the :c:func:`RingInit()` function. They +are initialized to be a singleton ring (:mps:ref:`.def.singleton`). + +.. c:function:: void RingFinish(Ring ring) + +:mps:tag:`finish` Rings are finished with the :c:func:`RingFinish()` function. A +ring must be a singleton ring before it can be finished (it is an +error to attempt to finish a non-singleton ring). + + +Iteration +--------- + +.. c:function:: RING_FOR(Ring node, Ring ring, Ring next) + +:mps:tag:`for` A macro is used for iterating over the elements in a ring. +This macro is called :c:func:`RING_FOR()`. :c:func:`RING_FOR()` takes three arguments. +The first is an iteration variable: ``node``. The second is the +"parent" element in the ring: ``ring``. The third is a variable used +by the iterator for working state (it holds a pointer to the next +node): ``next``. All arguments must be of type :c:type:`Ring`. The ``node`` +and ``next`` variables must be declared and in scope already. All +elements except for the "parent" element are iterated over. The macro +expands to a ``for`` statement. During execution of the loop, the +``node`` variable (the first argument to the macro) will be the value +of successive elements in the Ring (at the beginning of the statement +in the body of the loop). + +:mps:tag:`for.error` It is an error (possibly unchecked) for the ``node`` +and ``next`` variables to be modified except implicitly by using this +iterator. + +:mps:tag:`for.safe` It is safe to delete the current node during the +iteration. + +:mps:tag:`for.ex` An example:: + + Ring node, nextNode; + RING_FOR(node, &foo->barRing, nextNode) { + Bar bar = RING_ELT(Bar, FooRing, node); + frob(bar); + } + +:mps:tag:`for.ex.elt` Notice the idiomatic use of :c:func:`RING_ELT()` which is +almost universal when using :c:func:`RING_FOR()`. + + +Subclass +-------- + +.. c:function:: RING_ELT(type, field, Ring node) + +:mps:tag:`elt` :c:func:`RING_ELT()` is a macro that converts a pointer to a ring +structure into a pointer to the enclosing parent structure. +:c:func:`RING_ELT()` has three arguments which are, in order: ``type``, the +type of a pointer to the enclosing structure, ``field``, the name of +the ring structure field within it, ``ring``, the ring node. The +result is a pointer to the enclosing structure. + +.. note:: :c:func:`RING_ELT()` does not work for arrays of rings. + + +Append / Remove +--------------- + +.. c:function:: void RingAppend(Ring ring, Ring new) + +:mps:tag:`append` :c:func:`RingAppend()` appends a singleton ring to a ring (such +that the newly added element will be last in the iteration sequence). + +.. c:function:: void (RingInsert)(Ring ring, Ring new) + +:mps:tag:`insert` :c:func:`RingInsert()` adds a singleton rung to a ring (such that +the newly added element will be first in the iteration sequence). + +.. c:function:: void (RingRemove)(Ring old) + +:mps:tag:`remove` :c:func:`RingRemove()` removes an element from a ring. The newly +removed element becomes a singleton ring. It is an error for the +element to already be a singleton. + +:mps:tag:`improve.join` It would be possible to add a :c:func:`RingJoin()` operation +that joined two rings. This is not done as it is not required. + + +Naming +------ + +:mps:tag:`naming` By convention, when one structure ``Foo`` contains one +ring of ``Bar`` structures, the field in ``Foo`` is usually known as +``barRing``, and the field in ``Bar`` is known as ``fooRing``. If the +``Foo`` structure contains more than one ring of ``Bar`` structures, +then they will have names such as ``spongRing`` and ``frobRing``. + + +Deques +------ + +This section documents where rings differ significantly from deques. + +:mps:tag:`head` Deques used a distinguished head structure for the head of +the ring. Rings still have a separate head structure, but it is not +distinguished by type. + + +Defects +------- + +This section documents known defects with the current design. + +:mps:tag:`app_for.misuse` It is easy to pass :c:func:`RingAppend()` and +:c:func:`RING_FOR()` the arguments in the wrong order as all the arguments +have the same type. see :mps:ref:`.head`. + +:mps:tag:`check.improve` There is no method for performing a full integrity +check. This could be added. + + diff --git a/mps/manual/html/_sources/design/root.txt b/mps/manual/html/_sources/design/root.txt index b42b179d6b0..26c74156c05 100644 --- a/mps/manual/html/_sources/design/root.txt +++ b/mps/manual/html/_sources/design/root.txt @@ -1,6 +1,82 @@ +.. _design-root: + + .. index:: pair: root manager; design -.. _design-root: -.. include:: ../../converted/root.rst +Root manager +============ + +.. mps:prefix:: design.mps.root + + +Basics +------ + +:mps:tag:`root.def` The root node of the object graph is the node which +defines whether objects are accessible, and the place from which the +mutator acts to change the graph. In the MPS, a root is an object +which describes part of the root node. The root node is the total of +all the roots attached to the space. + +.. note:: + + Note that this combines two definitions of root: the accessibility + is what defines a root for tracing (see analysis.tracer.root.* and + the mutator action for barriers (see analysis.async-gc.root). + Pekka P. Pirinen, 1998-03-20. + +:mps:tag:`root.repr` Functionally, roots are defined by their scanning +functions. Roots *could* be represented as function closures: that is, +a pointer to a C function and some auxiliary fields. The most general +variant of roots is just that. However, for reasons of efficiency, +some special variants are separated out. + + +Details +------- + +Creation +........ + +:mps:tag:`create` A root becomes "active" as soon as it is created. + +:mps:tag:`create.col` The root inherits its colour from the mutator, since +it can only contain references copied there by the mutator from +somewhere else. If the mutator is grey for a trace when a root is +created then that root will be used to determine accessibility for +that trace. More specifically, the root will be scanned when that +trace flips. + +Destruction +........... + +:mps:tag:`destroy` It's OK to destroy a root at any time, except perhaps +concurrently with scanning it, but that's prevented by the arena lock. +If a root is destroyed the references in it become invalid and +unusable. + +Invariants +.......... + +:mps:tag:`inv.white` Roots are never white for any trace, because they +cannot be condemned. + +:mps:tag:`inv.rank` Roots always have a single rank. A root without ranks +would be a root without references, which would be pointless. The +tracer doesn't support multiple ranks in a single colour. + +Scanning +........ + +:mps:tag:`method` Root scanning methods are provided by the client so that +the MPS can locate and scan the root set. See protocol.mps.root for +details. + +.. note:: + + There are some more notes about root methods in + meeting.qa.1996-10-16. + + diff --git a/mps/manual/html/_sources/design/scan.txt b/mps/manual/html/_sources/design/scan.txt index 68c52fa8e22..ef6b29ceb35 100644 --- a/mps/manual/html/_sources/design/scan.txt +++ b/mps/manual/html/_sources/design/scan.txt @@ -1,6 +1,104 @@ +.. _design-scan: + + .. index:: pair: generic scanner; design -.. _design-scan: -.. include:: ../../converted/scan.rst +The generic scanner +=================== + +.. mps:prefix:: design.mps.scan + + +Summaries +--------- + +Scanned summary +............... + +:mps:tag:`summary.subset` The summary of reference seens by scan +(ss.unfixedSummary) is a subset of the summary previously computed +(SegSummary). + +There are two reasons that it is not an equality relation: + +1. If the segment has had objects forwarded onto it then its summary + will get unioned with the summary of the segment that the object + was forwarded from. This may increase the summary. The forwarded + object of course may have a smaller summary (if such a thing were + to be computed) and so subsequent scanning of the segment may + reduce the summary. (The forwarding process may erroneously + introduce zones into the destination's summary). + +2. A write barrier hit will set the summary to ``RefSetUNIV``. + +The reason that ss.unfixedSummary is always a subset of the previous +summary is due to an "optimization" which has not been made in +``TraceFix``. See impl.c.trace.fix.fixed.all. + + +Partial scans +............. + +:mps:tag:`clever-summary` With enough cleverness, it's possible to have +partial scans of condemned segments contribute to the segment summary. + +.. note:: + + We had a system which nearly worked -- see MMsrc(MMdevel_poolams + at 1997/08/14 13:02:55 BST), but it did not handle the situation + in which a segment was not under the write barrier when it was + condemned. + +:mps:tag:`clever-summary.acc` Each time we partially scan a segment, we +accumulate the post-scan summary of the scanned objects into a field +in the group, called 'summarySoFar'. The post-scan summary is (summary +\ white) U fixed. + +:mps:tag:`clever-summary.acc.condemn` The cumulative summary is only +meaningful while the segment is condemned. Otherwise it is set to +``RefSetEMPTY`` (a value which we can check). + +:mps:tag:`clever-summary.acc.reclaim` Then when we reclaim the segment, we +set the segment summary to the cumulative summary, as it is a +post-scan summary of all the scanned objects. + +:mps:tag:`clever-summary.acc.other-trace` If the segment is scanned by +another trace while it is condemned, the cumulative summary must be +set to the post-scan summary of this scan (otherwise it becomes +out-of-date). + +:mps:tag:`clever-summary.scan` The scan summary is expected to be a summary +of all scanned references in the segment. We don't know this +accurately until we've scanned everything in the segment. So we add in +the segment summary each time. + +:mps:tag:`clever-summary.scan.fix` TraceScan also expects the scan state +fixed summary to include the post-scan summary of all references which +were white. Since we don't scan all white references, we need to add +in an approximation to the summary of all white references which we +didn't scan. This is the intersection of the segment summary and the +white summary. + +:mps:tag:`clever-summary.wb` If the cumulative summary is smaller than the +mutator's summary, a write-barrier is needed to prevent the mutator +from invalidating it. This means that sometimes we'd have to put the +segment under the write-barrier at condemn, which might not be very +efficient + +.. note:: + + This is not an operation currently available to pool class + implementations Pekka P. Pirinen, 1998-02-26. + +:mps:tag:`clever-summary.method.wb` We need a new pool class method, called +when the write barrier is hit (or possibly any barrier hit). The +generic method will do the usual TraceAccess work, the trivial method +will do nothing. + +:mps:tag:`clever-summary.acc.wb` When the write barrier is hit, we need to +correct the cumulative summary to the mutator summary. This is +approximated by setting the summary to ``RefSetUNIV``. + + diff --git a/mps/manual/html/_sources/design/seg.txt b/mps/manual/html/_sources/design/seg.txt index a3b4889fd91..7dae28c1715 100644 --- a/mps/manual/html/_sources/design/seg.txt +++ b/mps/manual/html/_sources/design/seg.txt @@ -1,6 +1,315 @@ +.. _design-seg: + + .. index:: pair: segments; design -.. _design-seg: -.. include:: ../../converted/seg.rst +Segment data structure +====================== + +.. mps:prefix:: design.mps.seg + + +Introduction +------------ + +:mps:tag:`intro` This document describes the MPS Segment data structure. + + +Overview +-------- + +:mps:tag:`over.segments` Segments are the basic units of tracing and +shielding. The MPM also uses them as units of scanning and colour, +although pool classes may subdivide segments and be able to maintain +colour on a finer grain (down to the object level, for example). + +:mps:tag:`over.objects` The mutator's objects are stored in segments. +Segments are contiguous blocks of memory managed by some pool. + +:mps:tag:`segments.pool` The arrangement of objects within a segment is +determined by the class of the pool which owns the segment. The pool +is associated with the segment indirectly via the first tract of the +segment. + +:mps:tag:`over.memory` The relationship between segments and areas of memory +is maintained by the segment module. Pools acquire tracts from the +arena, and release them back to the arena when they don't need them +any longer. The segment module can associate contiguous tracts owned +by the same pool with a segment. The segment module provides the +methods SegBase, SegLimit, and SegSize which map a segment onto the +addresses of the memory block it represents. + +:mps:tag:`over.hierarchy` The Segment datastructure is designed to be +subclassable (see design.mps.protocol). The basic segment class +(:c:type:`Seg`) supports colour and protection for use by the tracer, as +well as support for a pool ring, and all generic segment functions. +Clients may use :c:type:`Seg` directly, but will most probably want to use a +subclass with additional properties. + +:mps:tag:`over.hierarchy.gcseg` The segment module provides ``GCSeg`` - a +subclass of :c:type:`Seg` which has full support for GC including buffering +and the ability to be linked onto the grey ring. + + +Data Structure +-------------- + +.. c:type:: struct SegStruct *Seg +.. c:type:: struct GCSegStruct *GCSeg + +The implementations are as follows:: + + typedef struct SegStruct { /* segment structure */ + Sig sig; /* impl.h.misc.sig */ + SegClass class; /* segment class structure */ + Tract firstTract; /* first tract of segment */ + RingStruct poolRing; /* link in list of segs in pool */ + Addr limit; /* limit of segment */ + unsigned depth : SHIELD_DEPTH_WIDTH; /* see impl.c.shield.def.depth */ + AccessSet pm : AccessMAX; /* protection mode, impl.c.shield */ + AccessSet sm : AccessMAX; /* shield mode, impl.c.shield */ + TraceSet grey : TRACE_MAX; /* traces for which seg is grey */ + TraceSet white : TRACE_MAX; /* traces for which seg is white */ + TraceSet nailed : TRACE_MAX; /* traces for which seg has nailed objects */ + RankSet rankSet : RankMAX; /* ranks of references in this seg */ + } SegStruct; + + + typedef struct GCSegStruct { /* GC segment structure */ + SegStruct segStruct; /* superclass fields must come first */ + RingStruct greyRing; /* link in list of grey segs */ + RefSet summary; /* summary of references out of seg */ + Buffer buffer; /* non-NULL if seg is buffered */ + Sig sig; /* design.mps.sig */ + } GCSegStruct; + + +:mps:tag:`field.rankSet` The ``rankSet`` field represents the set of ranks +of the references in the segment. It is initialized to empty by +:c:func:`SegInit()`. + +:mps:tag:`field.rankSet.single` The Tracer only permits one rank per segment +[ref?] so this field is either empty or a singleton. + +:mps:tag:`field.rankSet.empty` An empty ``rankSet`` indicates that there are +no references. If there are no references in the segment then it +cannot contain black or grey references. + +:mps:tag:`field.rankSet.start` If references are stored in the segment then +it must be updated, along with the summary (:mps:ref:`.field.summary.start`). + +:mps:tag:`field.depth` The ``depth`` field is used by the Shield +(impl.c.shield) to manage protection of the segment. It is initialized +to zero by :c:func:`SegInit()`. + +:mps:tag:`field.sm` The ``sm`` field is used by the Shield (impl.c.shield) +to manage protection of the segment. It is initialized to +``AccessSetEMPTY`` by :c:func:`SegInit()`. + +:mps:tag:`field.pm` The ``pm`` field is used by the Shield (impl.c.shield) +to manage protection of the segment. It is initialized to +``AccessSetEMPTY`` by :c:func:`SegInit()`. The field is used by both the +shield and the ANSI fake protection (impl.c.protan). + +:mps:tag:`field.black` The ``black`` field is the set of traces for which +there may be black objects (that is, objects containing references, +but no references to white objects) in the segment. More precisely, if +there is a black object for a trace in the segment then that trace +will appear in the ``black`` field. It is initialized to +``TraceSetEMPTY`` by :c:func:`SegInit()`. + +:mps:tag:`field.grey` The ``grey`` field is the set of traces for which +there may be grey objects (i.e containing references to white objects) +in the segment. More precisely, if there is a reference to a white +object for a trace in the segment then that trace will appear in the +``grey`` field. It is initialized to ``TraceSetEMPTY`` by :c:func:`SegInit()`. + +:mps:tag:`field.white` The ``white`` field is the set of traces for which +there may be white objects in the segment. More precisely, if there is +a white object for a trace in the segment then that trace will appear +in the ``white`` field. It is initialized to ``TraceSetEMPTY`` by +:c:func:`SegInit()`. + +:mps:tag:`field.summary` The ``summary`` field is an approximation to the +set of all references in the segment. If there is a reference ``R`` in +the segment, then ``RefSetIsMember(summary, R)`` is :c:macro:`TRUE`. The +summary is initialized to ``RefSetEMPTY`` by :c:func:`SegInit()`. + +:mps:tag:`field.summary.start` If references are stored in the segment then +it must be updated, along with ``rankSet`` (:mps:ref:`.field.rankSet.start`). + +:mps:tag:`field.buffer` The ``buffer`` field is either :c:macro:`NULL`, or points +to the descriptor structure of the buffer which is currently +allocating in the segment. The field is initialized to :c:macro:`NULL` by +:c:func:`SegInit()`. + +:mps:tag:`field.buffer.owner` This buffer must belong to the same pool as +the segment, because only that pool has the right to attach it. + + +Interface +--------- + +Splitting and merging +..................... + +:mps:tag:`split-and-merge` There is support for splitting and merging +segments, to give pools the flexibility to rearrange their tracts +among segments as they see fit. + +.. c:function:: Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at, Bool withReservoirPermit, ...) + +:mps:tag:`split` If successful, segment ``seg`` is split at address ``at``, +yielding two segments which are returned in segLoReturn and +segHiReturn for the low and high segments respectively. The base of +the low segment is the old base of ``seg``. The limit of the low +segment is ``at``. The base of the high segment is ``at``. This limit +of the high segment is the old limit of ``seg``. ``seg`` is +effectively destroyed during this operation (actually, it might be +reused as one of the returned segments). Segment subclasses may make +use of the optional arguments; the built-in classes do not. + +:mps:tag:`split.invariants` The client must ensure some invariants are met +before calling :c:func:`SegSplit()`: + +- :mps:tag:`split.inv.align` ``at`` must be appropriately aligned to the + arena alignment, and lie between the base and limit of ``seg``. + Justification: the split segments cannot be represented if this is + not so. + +- :mps:tag:`split.inv.buffer` If ``seg`` is attached to a buffer, the + buffered region must not include address ``at``. Justification: the + segment module is not in a position to know how (or whether) a pool + might wish to split a buffer. This permits the buffer to remain + attached to just one of the returned segments. + +:mps:tag:`split.state` Except as noted above, the segments returned have the +same properties as ``seg``. That is, their colour, summary, rankset, +nailedness etc. are set to the values of ``seg``. + +.. c:function:: Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi, Bool withReservoirPermit, ...) + +:mps:tag:`merge` If successful, segments ``segLo`` and ``segHi`` are merged +together, yielding a segment which is returned in mergedSegReturn. +``segLo`` and ``segHi`` are effectively destroyed during this +operation (actually, one of them might be reused as the merged +segment). Segment subclasses may make use of the optional arguments; +the built-in classes do not. + +:mps:tag:`merge.invariants` The client must ensure some invariants are met +before calling :c:func:`SegMerge()`: + +- :mps:tag:`merge.inv.abut` The limit of ``segLo`` must be the same as the + base of ``segHi``. Justification: the merged segment cannot be + represented if this is not so. + +- :mps:tag:`merge.inv.buffer` One or other of ``segLo`` and ``segHi`` may + attached to a buffer, but not both. Justification: the segment + module does not support attachment of a single seg to 2 buffers. + +- :mps:tag:`merge.inv.similar` ``segLo`` and ``segHi`` must be sufficiently + similar. Two segments are sufficiently similar if they have + identical values for each of the following fields: ``class``, + ``sm``, ``grey``, ``white``, ``nailed``, ``rankSet``. Justification: + there is no single choice of behaviour for cases where these fields + are not identical. The pool class must make it's own choices about + this if it wishes to permit more flexible merging. If so, it should + be a simple matter for the pool to arrange for the segments to look + sufficiently similar before calling :c:func:`SegMerge()`. + +:mps:tag:`merge.state` The merged segment will share the same state as +``segLo`` and ``segHi`` for those fields which are identical (see +:mps:ref:`.merge.inv.similar`). The summary will be the union of the summaries +of ``segLo`` and ``segHi``. + + +Extensibility +------------- + +Splitting and merging +..................... + +.. c:type:: Res (*SegSplitMethod)(Seg seg, Seg segHi, Addr base, Addr mid, Addr limit, Bool withReservoirPermit) + +:mps:tag:`method.split` Segment subclasses may extend the support for +segment splitting by defining their own "split" method. On entry, +``seg`` is a segment with region ``[base,limit)``, ``segHi`` is +uninitialized, ``mid`` is the address at which the segment is to be +split. The method is responsible for destructively modifying ``seg`` +and initializing ``segHi`` so that on exit ``seg`` is a segment with +region ``[base,mid)`` and ``segHi`` is a segment with region +``[mid,limit)``. Usually a method would only directly modify the +fields defined for the segment subclass. This might involve +allocation, which may use the reservoir if ``withReservoirPermit`` is +:c:macro:`TRUE`. + +:mps:tag:`method.split.next` A split method should always call the next +method, either before or after any class-specific code (see +design.mps.protocol :mps:ref:`.overview.next-method`). + +.. c:type:: Res (*SegMergeMethod)(Seg seg, Seg segHi, Addr base, Addr mid, Addr limit, Bool withReservoirPermit) + +:mps:tag:`method.merge` Segment subclasses may extend the support for +segment merging by defining their own ``merge`` method. On entry, +``seg`` is a segment with region ``[base,mid)``, ``segHi`` is a +segment with region ``[mid,limit)``, The method is responsible for +destructively modifying ``seg`` and finishing ``segHi`` so that on +exit ``seg`` is a segment with region ``[base,limit)`` and ``segHi`` +is garbage. Usually a method would only modify the fields defined for +the segment subclass. This might involve allocation, which may use the +reservoir if ``withReservoirPermit`` is :c:macro:`TRUE`. + +:mps:tag:`method.merge.next` A merge method should always call the next +method, either before or after any class-specific code (see +design.mps.protocol.overview.next-method). + +:mps:tag:`split-merge.shield` Split and merge methods may assume that the +segments they are manipulating are not in the shield cache. + +:mps:tag:`split-merge.shield.flush` The shield cache is flushed before any +split or merge methods are invoked. + +:mps:tag:`split-merge.shield.re-flush` If a split or merge method performs +an operation on a segment which might cause the segment to be cached, +the method must flush the shield cache before returning or calling +another split or merge method. + +:mps:tag:`split-merge.fail` Split and merge methods might fail, in which +case segments ``seg`` and ``segHi`` must be equivalently valid and +configured at exit as they were according to the entry conditions. +It's simplest if the failure can be detected before calling the next +method (for example, by allocating any objects early in the method). + +:mps:tag:`split-merge.fail.anti` If it's not possible to detect failure +before calling the next method, the appropriate anti-method must be +used (see design.mps.protocol.guide.fail.after-next). Split methods +are anti-methods for merge methods, and vice-versa. + +:mps:tag:`split-merge.fail.anti.constrain` In general, care should be taken +when writing split and merge methods to ensure that they really are +anti-methods for each other. The anti-method must not fail if the +initial method succeeded. The anti-method should reverse any side +effects of the initial method, except where it's known to be safe to +avoid this (see :mps:ref:`.split-merge.fail.summary` for an example of a safe +case). + +:mps:tag:`split-merge.fail.anti.no` If this isn't possible (it might not be) +then the methods won't support after-next failure. This fact should be +documented, if the methods are intended to support further +specialization. Note that using va_arg with the ``args`` parameter is +sufficient to make it impossible to reverse all side effects. + +:mps:tag:`split-merge.fail.summary` The segment summary might not be +restored exactly after a failed merge operation. Each segment would be +left with a summary which is the union of the original summaries (see +:mps:ref:`.merge.state`). This increases the conservatism in the summaries, +but is otherwise safe. + +:mps:tag:`split-merge.unsupported` Segment classes need not support segment +merging at all. The function :c:func:`SegClassMixInNoSplitMerge()` is supplied +to set the split and merge methods to unsupporting methods that will +report an error in checking varieties. + + diff --git a/mps/manual/html/_sources/design/shield.txt b/mps/manual/html/_sources/design/shield.txt new file mode 100644 index 00000000000..653937a2449 --- /dev/null +++ b/mps/manual/html/_sources/design/shield.txt @@ -0,0 +1,167 @@ +.. _design-shield: + + +.. index:: + pair: shield; design + + +Shield +====== + +.. mps:prefix:: design.mps.shield + + +Introduction +------------ + +:mps:tag:`intro` This document contains a guide to the MPS Shield. There is +no historical initial design, but in its place there are some early +ideas and discussions: see :mps:ref:`.ideas`. + +:mps:tag:`readership` Any MPS developer. Not confidential. + + +Overview +-------- + +:mps:tag:`over` For incremental collection, we need *separate control* of +collector access and mutator (client) access to memory. The collector +must be able to incrementally scan objects, without the mutator being +able to see them yet. + +Unfortunately common OSs do not support different access levels +(protection maps) for different parts of the same process. + +The MPS Shield is an abstraction that does extra work to overcome this +limitation, and give the rest of the MPS the illusion that we can +control collector and mutator access separately. + + +Control of mutator access +------------------------- + +The MPS uses :c:func:`ShieldRaise()` and :c:func:`ShieldLower()` to forbid or +permit the mutator access to object memory (that is, memory allocated +by MPS). + +.. c:function:: void ShieldRaise(Arena arena, Seg seg, AccessSet mode) + + Prevent the mutator accessing the memory in the specified mode + (``AccessREAD``, ``AccessWRITE``, or both). + +.. c:function:: void ShieldLower(Arena arena, Seg seg, AccessSet mode) + + Allow the mutator to access the memory in the specified mode + (``AccessREAD``, ``AccessWRITE``, or both). + +If the mutator attempts an access that hits a shield, the MPS gets a +barrier hit (in the form of a fault, interrupt, exception), quickly +does some necessary work, and then makes the access succeed. + +Some objects (for example registers) cannot be hardware protected: the +only way to prevent mutator access to them is to halt all mutator +threads. The MPS uses :c:func:`ShieldSuspend()` and :c:func:`ShieldResume()` to do +this. + +.. c:function:: void ShieldSuspend(Arena arena) + + Stop all registered mutator threads. + +.. c:function:: void ShieldResume(Arena arena) + + Resume all registered mutator threads. + + +Control of collector access +--------------------------- + +When the collector wants to access object memory (that is, memory +allocated by MPS), it must first call :c:func:`ShieldEnter()`, then wrap any +accesses with a :c:func:`ShieldExpose()` and :c:func:`ShieldCover()` pair, and +finally call :c:func:`ShieldLeave()`. + +:c:func:`ShieldEnter()` and :c:func:`ShieldLeave()` are called by :c:func:`ArenaEnter()` +and :c:func:`ArenaLeave()` (approximately) -- so the shield is always +entered when we are within MPS code (approximately). + +:c:func:`ShieldExpose()` might for example be called around: + +- format-scan (when scanning); +- format-skip (when marking grains in a non-moving fix); +- format-isMoved and :c:func:`AddrCopy()` (during a copying fix); +- format-pad (during reclaim). + +Note that there is no need to call :c:func:`ShieldExpose()` when accessing +pool management memory such as bit tables. This is not object +memory, is never (legally) accessed by the mutator, and so is never +shielded. + +On common operating systems, the only way to allow collector access is +to allow access from the whole process, including the mutator. So if +the Shield is asked to allow collector access but deny mutator access, +it will halt all mutator threads to prevent any mutator access. The +Shield performs suspension and restart; normal collector code does not +need to worry about it. + +Collector code can make multiple sequential, overlapping, or nested +calls to :c:func:`ShieldExpose()` on the same segment, as long as each is +balanced by a corresponding :c:func:`ShieldCover()` before :c:func:`ShieldLeave()` +is called). A usage count is maintained on each segment in +``seg->depth``: a positive "depth" means a positive number of +outstanding *reasons* why the segment must be exposed to the collector. +When the usage count reaches zero, there is no longer any reason the +segment should be unprotected, and the Shield could re-instate +hardware protection. + +However, as a performance-improving hysteresis, the Shield defers +re-protection, maintaining a cache of the last ``ShieldCacheSIZE`` +times a segment no longer had a reason to be collector-accessible. +Presence in the cache counts as a reason: segments in the cache have +``seg->depth`` increased by one. As segments get pushed out of the +cache, or at :c:func:`ShieldLeave()`, this artificial reason is +decremented from ``seg->depth``, and (if ``seg->depth`` is now zero) +the deferred reinstatement of hardware protection happens. + +So whenever hardware protection is temporarily removed to allow +collector access, there is a *nurse* that will ensure this protection +is re-established: the nurse is either the balancing :c:func:`ShieldCover()` +call in collector code, or an entry in the shield cache. + +.. note:: + + 1. Why is there a fixed-size cache? This is not the simple + approach! All we need is a chain of segs that might need their + hardware protection to be sync'd with their shield mode. Head + in the shield, and one pointer in each seg struct. I guess we + try hard to avoid bloating :c:type:`SegStruct` (to maintain residency + in the processor cache). But is 16 the right size? A cache-miss + wastes two kernel calls. + + 2. I don't like the cache code. For example, why does + :c:func:`ShieldFlush()` break out early if ``arena->shDepth`` is 0? + This should never happen until the cache is completely flushed, + that is, we have reached ``shCacheLimit``. Why does + :c:func:`ShieldFlush()` not reset ``shCacheLimit``? Why does + :c:func:`flush()` silently accept :c:macro:`NULL` cache entries? + + 3. Why is ``seg->depth`` never checked for overflow? It is only a + 4-bit-wide bit field, currently. + + Richard Kistruck, 2006-12-19. + + +Initial ideas +------------- + +:mps:tag:`ideas` There never was an initial design document, but +[RB_1995-11-29]_ and [RB_1995-11-30]_ contain some initial ideas. + + +References +---------- + +.. [RB_1995-11-29] Richard Brooksby. Harlequin. 1995-11-29. "`Shield protocol for barriers `__". + +.. [RB_1995-11-30] Richard Brooksby. Harlequin. 1995-11-30. "`Exegesis of Incremental Tracing `__". + + diff --git a/mps/manual/html/_sources/design/sig.txt b/mps/manual/html/_sources/design/sig.txt index 674a47e2bfd..d450855903d 100644 --- a/mps/manual/html/_sources/design/sig.txt +++ b/mps/manual/html/_sources/design/sig.txt @@ -1,7 +1,159 @@ +.. _design-sig: + + .. index:: pair: structure signatures; design single: signatures -.. _design-sig: -.. include:: ../../converted/sig.rst +Signatures in the MPS +===================== + +.. mps:prefix:: design.mps.sig + pair: structure signatures; design + single: signatures + + +Introduction +------------ +Integrity of data structures is absolutely critical to the cost of +deploying the Memory Pool System. Memory corruption and memory +management bugs are incredibly hard to detect and debug, often +manifesting themselves hours or days after they occur. One of the key +ways the MPS detects corruption or the passing of illegal data is using +*signatures*. This simple technique has proved invaluable at catching +defects early. + + +Overview +-------- +Signatures are `magic numbers`_ which are written into structures when +they are created and invalidated (by overwriting with ``SigInvalid``) +when they are destroyed. They provide a limited form of run-time type +checking and dynamic scope checking. They are a simplified form of +"Structure Marking", a technique used in the Multics filesystem +[THVV_1995]_. + +.. _`magic numbers`: http://en.wikipedia.org/wiki/Magic_number_(programming) + + +Definitions +----------- +Nearly every structure should start with a field of type :c:type:`Sig` with the name +``sig``. For example:: + + typedef struct mps_message_s { + Sig sig; /* */ + Arena arena; /* owning arena */ + MessageClass class; /* Message Class Structure */ + Clock postedClock; /* mps_clock() at post time, or 0 */ + RingStruct queueRing; /* Message queue ring */ + } MessageStruct; + +There must also be a definition for the valid value for that signature:: + + #define MessageSig ((Sig)0x5193e559) /* SIG MESSaGe */ + +This is a 32-bit hex constant, spelled according to guide.hex.trans_:: + + ABCDEFGHIJKLMNOPQRSTUVWXYZ + ABCDEF9811C7340BC6520F3812 + +.. _guide.hex.trans: guide.hex.trans + +This allows the structure to be recognised when looking at memory in a hex +dump or memory window, or found using memory searches. + + +Init and Finish +--------------- +When the structure is initialised, the signature is initialised as the +*last* action, just before validating it. (Think of it as putting your +signature at the bottom of a document to say it's done.) This ensures +that the structure will appear invalid until it is completely initialized +and ready to use. For example:: + + void MessageInit(...) { + ... + message->arena = arena; + message->class = class; + RingInit(&message->queueRing); + message->postedClock = 0; + message->sig = MessageSig; + AVERT(Message, message); + } + +When the structure is finished, the signature is invalidated as the +*first* action, ensuring that the structure appears invalid while it is +being torn down. For example:: + + void MessageFinish(Message message) + { + AVERT(Message, message); + AVER(RingIsSingle(&message->queueRing)); + + message->sig = SigInvalid; + RingFinish(&message->queueRing); + } + +Do not do anything else with signatures. See :mps:ref:`.rule.purpose`. + + +Checking +-------- +The signature is checked in various ways. Every function that takes a +(pointer to) a signed structure should check its argument using the +:c:macro:`AVERT` macro. This macro has different definitions depending on how +the MPS is compiled (see design.mps.config.def.var_). It may simply check +the signature directly, or call the full checking function for the +structure. + +.. _design.mps.config.def.var: config#def-var + +The checking function for the structure should also validate the +signature as its first step using the :c:func:`CHECKS()` macro (see +check.h_). For example:: + + Bool MessageCheck(Message message) + { + CHECKS(Message, message); + CHECKU(Arena, message->arena); + CHECKD(MessageClass, message->class); + ... + +This combination makes it extremely difficult to get an object of the +wrong type, an uninitialized object, or a dead object, or a random +pointer into a function. + +.. _check.h: ../code/check.h + +Rules +----- +:mps:tag:`rule.purpose` **Do not** use signatures for any other purpose. For +example, don't use them to make any actual decisions within the code. +They must not be used to discriminate between structure variants (or +union members). They must not be used to try to detect *whether* a +structure has been initialised or finished. They are there to +double-check whether these facts are true. They lose their value as a +consistency check if the code uses them as well. + + +Tools +----- +:mps:tag:`test.uniq` The Unix command:: + + sed -n '/^#define [a-zA-Z]*Sig/s/[^(]*(/(/p' code/*.[ch] | sort | uniq -c + +will display all signatures defined in the MPS along with a count of how +many times they are defined. If any counts are greater than 1, then the +same signature value is being used for different signatures. This is +undesirable and the problem should be investigated. + + +References +---------- +.. [RB_1995-08-25] Richard Brooksby. Harlequin. 1995-08-25. "`design.mps.sig: The design of the Memory Pool System Signature System `__". + +.. [THVV_1995] Tom Van Vleck. 1995. "`Structure Marking `__". + + diff --git a/mps/manual/html/_sources/design/splay.txt b/mps/manual/html/_sources/design/splay.txt index 79400def8b5..2b1ab560fc2 100644 --- a/mps/manual/html/_sources/design/splay.txt +++ b/mps/manual/html/_sources/design/splay.txt @@ -1,6 +1,914 @@ +.. _design-splay: + + .. index:: pair: splay trees; design -.. _design-splay: -.. include:: ../../converted/splay.rst +Splay trees +=========== + +.. mps:prefix:: design.mps.splay + + +Introduction +------------ + +:mps:tag:`intro` This document explains the design of impl.c.splay, an +implementation of Splay Trees, including its interface and +implementation. + +:mps:tag:`readership` This document is intended for any MM developer. + +:mps:tag:`source` The primary sources for this design are paper.st85(0) and +paper.sleator96(0). Also as CBS is a client, design.mps.cbs. As +PoolMVFF is an indirect client, design.mps.poolmvff(1). Also, as +PoolMV2 is an (obsolescent?) indirect client, design.mps.poolmv2. + +:mps:tag:`background` The following background documents influence the design: +guide.impl.c.adt(0). + + +Overview +-------- + +:mps:tag:`overview` Splay trees are a form of binary tree where each access +brings the accessed element (or the nearest element) to the root of +the tree. The restructuring of the tree caused by the access gives +excellent amortised performance, as the splay tree adapts its shape to +usage patterns. Unused nodes have essentially no time overhead. + + +Definitions +----------- + +:mps:tag:`def.splay-tree` A "Splay Tree" is a self-adjusting binary tree as +described in paper.st85(0), paper.sleator96(0). + +:mps:tag:`def.node` A "node" is used in the typical datastructure sense to +mean an element of a tree (see also :mps:ref:`.type.splay.node`). + +:mps:tag:`def.key` A "key" is a value associated with each node; the keys +are totally ordered by a client provided comparator. + +:mps:tag:`def.comparator` A "comparator" is a function that compares keys to +determine their ordering (see also :mps:ref:`.type.splay.compare.method`). + +:mps:tag:`def.successor` Node *N1* is the "successor" of node *N2* if *N1* +and *N2* are both in the same tree, and the key of *N1* immediately +follows the key of *N2* in the ordering of all keys for the tree. + +:mps:tag:`def.left-child` Each node *N* contains a "left child", which is a +(possibly empty) sub-tree of nodes. The key of *N* is ordered after +the keys of all nodes in this sub-tree. + +:mps:tag:`def.right-child` Each node *N* contains a "right child", which is +a (possibly empty) sub-tree of nodes. The key of *N* is ordered before +the keys of all nodes in this sub-tree. + +:mps:tag:`def.neighbour` A node *N* which has key *Kn* is a "neighbour" of a +key *K* if either *Kn* is the first key in the total order which +compares greater than *K* or if *Kn* is the last key in the total +order which compares less than *K*. + +:mps:tag:`def.first` A node is the "first" node in a set of nodes if its key +compares less than the keys of all other nodes in the set. + +:mps:tag:`def.last` A node is the "last" node in a set of nodes if its key +compares greater than the keys of all other nodes in the set. + +:mps:tag:`def.client-property` A "client property" is a value that the +client may associate with each node in addition to the key (a block +size, for example). This splay tree implementation provides support +for efficiently finding the first or last nodes with suitably large +client property values. See also :mps:ref:`.prop` below. + + +Requirements +------------ + +:mps:tag:`req` These requirements are drawn from those implied by +design.mps.poolmv2, design.mps.poolmvff(1), design.mps.cbs(2) and +general inferred MPS requirements. + +:mps:tag:`req.order` Must maintain a set of abstract keys which is totally +ordered for a comparator. + +:mps:tag:`req.tree` The keys must be associated with nodes arranged in a +Splay Tree. + +:mps:tag:`req.splay` Common operations must balance the tree by splaying it, +to achieve low amortized cost (see paper.st85(0)). + +:mps:tag:`req.add` Must be able to add new members. This is a common +operation. + +:mps:tag:`req.remove` Must be able to remove members. This is a common +operation. + +:mps:tag:`req.locate` Must be able to locate a member, given a key. This is +a common operation. + +:mps:tag:`req.neighbours` Must be able to locate the neighbouring members +(in order) of a non-member, given a key (see :mps:ref:`.def.neighbour`). This +is a common operation. + +:mps:tag:`req.iterate` Must be able to iterate over all members in order +with reasonable efficiency. + +:mps:tag:`req.protocol` Must support detection of protocol violations. + +:mps:tag:`req.debug` Must support debugging of clients. + +:mps:tag:`req.stack` Must do all non-debugging operations with stack usage +bounded by a constant size. + +:mps:tag:`req.adapt` Must adapt to regularities in usage pattern, for better +performance. + +:mps:tag:`req.property` Must permit a client to associate a client property +(such as a size) with each node in the tree. + +:mps:tag:`req.property.change` Must permit a client to dynamically reassign +client properties to nodes in the tree. This is a common operation. + +:mps:tag:`req.property.find` Must support rapid finding of the first and +last nodes which have a suitably large value for their client +property. This is a common operation. + +:mps:tag:`req.root` Must be able to find the root of a splay tree (if one +exists). + + +External types +-------------- + +.. c:type:: struct SplayTreeStruct SplayTreeStruct +.. c:type:: struct SplayTreeStruct *SplayTree + +:mps:tag:`type.splay.tree` :c:type:`SplayTree` is the type of the main object at +the root of the splay tree. It is intended that the +:c:type:`SplayTreeStruct` can be embedded in another structure (see +:mps:ref:`.usage.client-tree` for an example). No convenience functions are +provided for allocation or deallocation. + +.. c:type:: struct SplayNodeStruct SplayNodeStruct +.. c:type:: struct SplayNodeStruct *SplayNode + +:mps:tag:`type.splay.node` :c:type:`SplayNode` is the type of a node of the splay +tree. :c:type:`SplayNodeStruct` contains no fields to store the key +associated with the node, or the client property. Again, it is +intended that the :c:type:`SplayNodeStruct` can be embedded in another +structure, and that this is how the association will be made (see +:mps:ref:`.usage.client-node` for an example). No convenience functions are +provided for allocation or deallocation. + +.. c:type:: Compare (*SplayCompareMethod)(void *key, SplayNode node) + +:mps:tag:`type.splay.compare.method` A function of type +:c:type:`SplayCompareMethod` is required to compare ``key`` with the key the +client associates with that splay tree node ``node``, and return the +appropriate Compare value (see :mps:ref:`.usage.compare` for an example). The +function compares a key with a node, rather than a pair of keys or +nodes as might seem more obvious. This is because the details of the +mapping between nodes and keys is left to the client (see +:mps:ref:`.type.splay.node`), and the splaying operations compare keys with +nodes (see :mps:ref:`.impl.splay`). + +.. c:type:: Res (*SplayNodeDescribeMethod)(SplayNode node, mps_lib_FILE *stream) + +:mps:tag:`type.splay.node.describe.method` A function of type +:c:type:`SplayNodeDescribeMethod` is required to write (via :c:func:`WriteF()`) a +client-oriented representation of the splay node. The output should be +non-empty, short, and without return characters. This is provided for +debugging purposes only. + +.. c:type:: Bool (*SplayTestNodeMethod)(SplayTree tree, SplayNode node, void *closureP, unsigned long closureS) + +:mps:tag:`type.splay.test.node.method` A function of type +:c:type:`SplayTestNodeMethod` required to determine whether the node itself +meets some client determined property (see :mps:ref:`.prop` and +:mps:ref:`.usage.test.node` for an example). Parameters ``closureP`` and +``closureS`` describe the environment for the function (see +:mps:ref:`.function.splay.find.first` and :mps:ref:`.function.splay.find.last`). + +.. c:type:: Bool (*SplayTestTreeMethod)(SplayTree tree, SplayNode node, void *closureP, unsigned long closureS) + +:mps:tag:`type.splay.test.tree.method` A function of type +:c:type:`SplayTestTreeMethod` is required to determine whether any of the +nodes in the sub-tree rooted at the given node meet some client +determined property (see :mps:ref:`.prop` and :mps:ref:`.usage.test.tree` for an +example). In particular, it must be a precise (not conservative) +indication of whether there are any nodes in the sub-tree for which +the ``testNode`` method (see :mps:ref:`.type.splay.test.node.method`) would +return :c:macro:`TRUE`. Parameters ``closureP`` and ``closureS`` describe the +environment for the function (see :mps:ref:`.function.splay.find.first` and +:mps:ref:`.function.splay.find.last`). + +.. c:type:: void (*SplayUpdateNodeMethod)(SplayTree tree, SplayNode node, SplayNode leftChild, SplayNode rightChild) + +:mps:tag:`type.splay.update.node.method` A function of type +:c:type:`SplayUpdateNodeMethod` is required to update any client +datastructures associated with a node to maintain some client +determined property (see :mps:ref:`.prop`) given that the children of the node +have changed. If the node does not have one or both children, then +:c:macro:`NULL` will be passed as the relevant parameter. (See +:mps:ref:`.usage.callback` for an example) + + +External functions +------------------ + +:mps:tag:`function.no-thread` The interface functions are not designed to be +either thread-safe or re-entrant. Clients of the interface are +responsible for synchronization, and for ensuring that client-provided +methods invoked by the splay module (:mps:ref:`.type.splay.compare.method`, +:mps:ref:`.type.splay.test.node.method`, :mps:ref:`.type.splay.test.tree.method`, +:mps:ref:`.type.splay.update.node.method`) do not call functions of the splay +module. + +.. c:function:: Bool SplayTreeCheck(SplayTree tree) + +:mps:tag:`function.splay.tree.check` This is a check function for the +SplayTree type (see guide.impl.c.adt.method.check & +design.mps.check(0)). + +.. c:function:: Bool SplayNodeCheck(SplayNode node) + +:mps:tag:`function.splay.node.check` This is a check function for the +:c:type:`SplayNode` type (see guide.impl.c.adt.method.check & +design.mps.check(0)). + +.. c:function:: void SplayTreeInit(SplayTree tree, SplayCompareMethod compare, SplayUpdateNodeMethod updateNode) + +:mps:tag:`function.splay.tree.init` This function initialises a +:c:type:`SplayTree` (see guide.impl.c.adt.method.init). It requires a +``compare`` method that defines a total ordering on nodes (see +:mps:ref:`.req.order`); the effect of supplying a compare method that does not +implement a total ordering is undefined. It also requires an +``updateNode`` method, which will be used to keep client properties up +to date when the tree structure changes; the value +``SplayTrivUpdateNode`` may be used for this method if there is no +need to maintain client properties. (See :mps:ref:`.usage.initialization` for +an example use). + +.. c:function:: void SplayTreeFinish(SplayTree tree) + +:mps:tag:`function.splay.tree.finish` This function clears the fields of a +:c:type:`SplayTree` (see guide.impl.c.adt.method.finish). Note that it does +not attempt to finish or deallocate any associated :c:type:`SplayNode` +objects; clients wishing to destroy a non-empty :c:type:`SplayTree` must +first explicitly descend the tree and call :c:func:`SplayNodeFinish()` on +each node from the bottom up. + +.. c:function:: void SplayNodeInit(SplayNode node) + +:mps:tag:`function.splay.node.init` This function initialises a +:c:type:`SplayNode` (see guide.impl.c.adt.method.init). + +.. c:function:: void SplayNodeFinish(SplayNode node) + +:mps:tag:`function.splay.node.finish` This function clears the fields of a +:c:type:`SplayNode` (see guide.impl.c.adt.method.finish). Note that it does +not attempt to finish or deallocate any referenced :c:type:`SplayNode` +objects (see.function.splay.tree.finish). + +.. c:function:: Bool SplayRoot(SplayNode *nodeReturn, SplayTree tree) + +:mps:tag:`function.splay.root` This function returns the root node of the +tree, if any (see :mps:ref:`.req.root`). If the tree is empty, :c:macro:`FALSE` is +returned and ``*nodeReturn`` is not changed. Otherwise, :c:macro:`TRUE` is +returned and ``*nodeReturn`` is set to the root. + +.. c:function:: Res SplayTreeInsert(SplayTree tree, SplayNode node, void *key) + +:mps:tag:`function.splay.tree.insert` This function is used to insert into a +splay tree a new node which is associated with the supplied key (see +:mps:ref:`.req.add`). It first splays the tree at the key. If an attempt is +made to insert a node that compares ``CompareEQUAL`` to an existing +node in the tree, then ``ResFAIL`` will be returned and the node will +not be inserted. (See :mps:ref:`.usage.insert` for an example use). + +.. c:function:: Res SplayTreeDelete(SplayTree tree, SplayNode node, void *key) + +:mps:tag:`function.splay.tree.delete` This function is used to delete from a +splay tree a node which is associated with the supplied key (see +:mps:ref:`.req.remove`). If the tree does not contain the given node, or the +given node does not compare ``CompareEQUAL`` with the given key, then +``ResFAIL`` will be returned, and the node will not be deleted. The +function first splays the tree at the given key. (See :mps:ref:`.usage.delete` +for an example use). + +.. c:function:: Res SplayTreeSearch(SplayNode *nodeReturn, SplayTree tree, void *key) + +:mps:tag:`function.splay.tree.search` This function searches the splay tree +for a node that compares ``CompareEQUAL`` to the given key (see +:mps:ref:`.req.locate`). It splays the tree at the key. It returns ``ResFAIL`` +if there is no such node in the tree, otherwise ``*nodeReturn`` will +be set to the node. + +.. c:function:: Res SplayTreeNeighbours(SplayNode *leftReturn, SplayNode *rightReturn, SplayTree tree, void *key) + +:mps:tag:`function.splay.tree.neighbours` This function searches a splay +tree for the two nodes that are the neighbours of the given key (see +:mps:ref:`.req.neighbours`). It splays the tree at the key. ``*leftReturn`` +will be the neighbour which compares less than the key if such a +neighbour exists; otherwise it will be :c:macro:`NULL`. ``*rightReturn`` will +be the neighbour which compares greater than the key if such a +neighbour exists; otherwise it will be :c:macro:`NULL`. The function returns +``ResFAIL`` if any node in the tree compares ``CompareEQUAL`` with the +given key. (See :mps:ref:`.usage.insert` for an example use). + +.. c:function:: SplayNode SplayTreeFirst(SplayTree tree, void *zeroKey) + +:mps:tag:`function.splay.tree.first` This function splays the tree at the +first node, and returns that node (see :mps:ref:`.req.iterate`). The supplied +key should compare ``CompareLESS`` with all nodes in the tree. It will +return :c:macro:`NULL` if the tree has no nodes. + +.. c:function:: SplayNode SplayTreeNext(SplayTree tree, SplayNode oldNode, void *oldKey) + +:mps:tag:`function.splay.tree.next` This function receives a node and key +and returns the successor node to that node (see :mps:ref:`.req.iterate`). +This function is intended for use in iteration when the received node +will be the current root of the tree, but is robust against being +interspersed with other splay operations (provided the old node still +exists). The supplied key must compare ``CompareEQUAL`` to the +supplied node. Note that use of this function rebalances the tree for +each node accessed. If many nodes are accessed as a result of multiple +uses, the resultant tree will be generally well balanced. But if the +tree was previously beneficially balanced for a small working set of +accesses, then this local optimization will be lost. (see +:mps:ref:`.future.parent`). + +.. c:function:: Res SplayTreeDescribe(SplayTree tree, mps_lib_FILE *stream, SplayNodeDescribeMethod nodeDescribe) + +:mps:tag:`function.splay.tree.describe` This function prints (using +``WriteF``) to the stream a textual representation of the given splay +tree, using ``nodeDescribe`` to print client-oriented representations +of the nodes (see :mps:ref:`.req.debug`). + +.. c:function:: Bool SplayFindFirst(SplayNode *nodeReturn, SplayTree tree, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, unsigned long closureS) + +:mps:tag:`function.splay.find.first` :c:func:`SplayFindFirst()` finds the first node +in the tree that satisfies some client property (as determined by the +``testNode`` and ``testTree`` methods) (see :mps:ref:`.req.property.find`). +``closureP`` and ``closureS`` are arbitrary values, and are passed to +the ``testNode`` and ``testTree`` methods which may use the values as +closure environments. If there is no satisfactory node, then :c:macro:`FALSE` +is returned, otherwise ``*nodeReturn`` is set to the node. (See +:mps:ref:`.usage.delete` for an example use). + +.. c:function:: Bool SplayFindFirst(SplayNode *nodeReturn, SplayTree tree, SplayTestNodeMethod testNode, SplayTestTreeMethod testTree, void *closureP, unsigned long closureS) + +:mps:tag:`function.splay.find.last` :c:func:`SplayFindLast()` finds the last node +in the tree that satisfies some client property (as determined by the +``testNode`` and ``testTree`` methods) (see :mps:ref:`.req.property.find`). +``closureP`` and ``closureS`` are arbitrary values, and are passed to +the ``testNode`` and ``testTree`` methods which may use the values as +closure environments. If there is no satisfactory node, then :c:macro:`FALSE` +is returned, otherwise ``*nodeReturn`` is set to the node. + +.. c:function:: void SplayNodeRefresh(SplayTree tree, SplayNode node, void *key) + +:mps:tag:`function.splay.node.refresh` :c:func:`SplayNodeRefresh()` must be called +whenever the client property (see :mps:ref:`.prop`) at a node changes (see +:mps:ref:`.req.property.change`). It will call the ``updateNode`` method on +the given node, and any other nodes that may require update. The +client key for the node must also be supplied; the function splays the +tree at this key. (See :mps:ref:`.usage.insert` for an example use). + + +Client-determined properties +---------------------------- + +:mps:tag:`prop` To support :mps:ref:`.req.property.find`, this splay tree +implementation provides additional features to permit clients to cache +maximum (or minimum) values of client properties for all the nodes in +a subtree. The splay tree implementation uses the cached values as +part of ``SplayFindFirst`` and ``SplayFindLast`` via the ``testNode`` +and ``testTree`` methods. The client is free to choose how to +represent the client property, and how to compute and store the cached +value. + +:mps:tag:`prop.update` The cached values depend upon the topology of the +tree, which may vary as a result of operations on the tree. The client +is given the opportunity to compute new cache values whenever +necessary, via the ``updateNode`` method (see +:mps:ref:`.function.splay.tree.init`). This happens whenever the tree is +restructured. The client may use the ``SplayNodeRefresh`` method to +indicate that the client attributes at a node have changed (see +:mps:ref:`.req.property.change`). A call to ``SplayNodeRefresh`` splays the +tree at the specified node, which may provoke calls to the +``updateNode`` method as a result of the tree restructuring. The +``updateNode`` method will also be called whenever a new splay node is +inserted into the tree. + +:mps:tag:`prop.example` For example, if implementing an address ordered tree +of free blocks using a splay tree, a client might choose to use the +base address of each block as the key for each node, and the size of +each block as the client property. The client can then maintain as a +cached value in each node the size of the largest block in the subtree +rooted at that node. This will permit a fast search for the first or +last block of at least a given size. See :mps:ref:`.usage.callback` for an +example ``updateNode`` method for such a client. + +:mps:tag:`prop.ops` The splay operations must cause client properties for +nodes to be updated in the following circumstances:- (see :mps:ref:`.impl` for +details): + +:mps:tag:`prop.ops.rotate` rotate left, rotate right -- We need to update +the value at the original root, and the new root, in that order. + +:mps:tag:`prop.ops.link` link left, link right -- We know that the line of +right descent from the root of the left tree and the line of left +descent from the root of the right tree will both need to be updated. +This is performed at the assembly stage. (We could update these chains +every time we do a link left or link right instead, but this would be +less efficient) + +:mps:tag:`prop.ops.assemble` assemble -- This operation also invalidates the +lines of right and left descent of the left and right trees +respectively which need to be updated (see below). It also invalidates +the root which must be updated last. + +:mps:tag:`prop.ops.assemble.reverse` To correct the chains of the left and +right trees without requiring stack or high complexity, we use a +judicious amount of pointer reversal. + +:mps:tag:`prop.ops.assemble.traverse` During the assembly, after the root's +children have been transplanted, we correct the chains of the left and +right trees. For the left tree, we traverse the right child line, +reversing pointers, until we reach the node that was the last node +prior to the transplantation of the root's children. Then we update +from that node back to the left tree's root, restoring pointers. +Updating the right tree is the same, mutatis mutandis. (See +:mps:ref:`.future.reverse` for an alternative approach). + + +Usage +----- + +:mps:tag:`usage` Here's a simple example of a client which uses a splay tree +to implement an address ordered tree of free blocks. The significant +client usages of the splay tree interface might look as follows:- + +:mps:tag:`usage.client-tree` Tree structure to embed a :c:type:`SplayTree` (see +:mps:ref:`.type.splay.tree`):: + + typedef struct FreeTreeStruct { + SplayTreeStruct splayTree; /* Embedded splay tree */ + /* no obvious client fields for this simple example */ + } FreeTreeStruct; + +:mps:tag:`usage.client-node` Node structure to embed a SplayNode (see :mps:ref:`.type.splay.node`):: + + typedef struct FreeBlockStruct { + SplayNodeStruct splayNode; /* embedded splay node */ + Addr base; /* base address of block is also the key */ + Size size; /* size of block is also the client property */ + Size maxSize; /* cached value for maximum size in subtree */ + } FreeBlockStruct; + +:mps:tag:`usage.callback` updateNode callback method (see +:mps:ref:`.type.splay.update.node.method`):: + + void FreeBlockUpdateNode(SplayTree tree, SplayNode node, + SplayNode leftChild, SplayNode rightChild) + { + /* Compute the maximum size of any block in this subtree. */ + /* The value to cache is the maximum of the size of this block, */ + /* the cached value for the left subtree (if any) and the cached */ + /* value of the right subtree (if any) */ + + FreeBlock freeNode = FreeBlockOfSplayNode(node); + + Size maxSize = freeNode.size; + + if(leftChild != NULL) { + FreeBlock leftNode = FreeBlockOfSplayNode(leftChild); + if(leftNode.maxSize > maxSize) + maxSize = leftNode->maxSize; + } + + if(rightChild != NULL) { + FreeBlock rightNode = FreeBlockOfSplayNode(rightChild); + if(rightNode.maxSize > maxSize) + maxSize = rightNode->maxSize; + } + + freeNode->maxSize = maxSize; + } + +:mps:tag:`usage.compare` Comparison function (see :mps:ref:`.type.splay.compare.method`):: + + Compare FreeBlockCompare(void *key, SplayNode node) { + Addr base1, base2, limit2; + FreeBlock freeNode = FreeBlockOfSplayNode(node); + + base1 = (Addr *)key; + base2 = freeNode->base; + limit2 = AddrAdd(base2, freeNode->size); + + if (base1 < base2) + return CompareLESS; + else if (base1 >= limit2) + return CompareGREATER; + else + return CompareEQUAL; + } + +:mps:tag:`usage.test.tree` Test tree function (see +:mps:ref:`.type.splay.test.tree.method`):: + + Bool FreeBlockTestTree(SplayTree tree, SplayNode node + void *closureP, unsigned long closureS) { + /* Closure environment has wanted size as value of closureS. */ + /* Look at the cached value for the node to see if any */ + /* blocks in the subtree are big enough. */ + + Size size = (Size)closureS; + FreeBlock freeNode = FreeBlockOfSplayNode(node); + return freeNode->maxSize >= size; + } + +:mps:tag:`usage.test.node` Test node function (see +:mps:ref:`.type.splay.test.node.method`):: + + Bool FreeBlockTestNode(SplayTree tree, SplayNode node + void *closureP, unsigned long closureS) { + /* Closure environment has wanted size as value of closureS. */ + /* Look at the size of the node to see if is big enough. */ + + Size size = (Size)closureS; + FreeBlock freeNode = FreeBlockOfSplayNode(node); + return freeNode->size >= size; + } + + :mps:tag:`usage.initialization` Client's initialization function (see + :mps:ref:`.function.splay.tree.init`): + void FreeTreeInit(FreeTree tree) { + /* Initialize the embedded splay tree. */ + SplayTreeInit(&tree->splayTree, FreeBlockCompare, FreeBlockUpdateNode); + } + +:mps:tag:`usage.insert` Client function to add a new free block into the +tree, merging it with an existing block if possible:: + + void FreeTreeInsert(FreeTree tree, Addr base, Addr limit) { + SplayTree splayTree = &tree->splayTree; + SplayNode leftNeighbour, rightNeighbour; + void *key = (void *)base; /* use the base of the block as the key */ + Res res; + + /* Look for any neighbouring blocks. (.function.splay.tree.neighbours) */ + res = SplayTreeNeighbours(&leftNeighbour, &rightNeighbour, + splayTree, key); + AVER(res == ResOK); /* this client doesn't duplicate free blocks */ + + /* Look to see if the neighbours are contiguous. */ + + if (leftNeighbour != NULL && + FreeBlockLimitOfSplayNode(leftNeighbour) == base) { + /* Inserted block is contiguous with left neighbour, so merge it. */ + /* The client housekeeping is left as an exercise to the reader. */ + /* This changes the size of a block, which is the client */ + /* property of the splay node. See :mps:ref:`.function.splay.node.refresh` */ + SplayNodeRefresh(tree, leftNeighbour, key); + + } else if (rightNeighbour != NULL && + FreeBlockBaseOfSplayNode(rightNeighbour) == limit) { + /* Inserted block is contiguous with right neighbour, so merge it. */ + /* The client housekeeping is left as an exercise to the reader. */ + /* This changes the size of a block, which is the client */ + /* property of the splay node. See :mps:ref:`.function.splay.node.refresh` */ + SplayNodeRefresh(tree, rightNeighbour, key); + + } else { + /* Not contiguous - so insert a new node */ + FreeBlock newBlock = (FreeBlock)allocate(sizeof(FreeBlockStruct)); + splayNode = &newBlock->splayNode; + + newBlock->base = base; + newBlock->size = AddrOffset(base, limit); + SplayNodeInit(splayNode); /* :mps:ref:`.function.splay.node.init` */ + /* :mps:ref:`.function.splay.tree.insert` */ + res = SplayTreeInsert(splayTree, splayNode, key); + AVER(res == ResOK); /* this client doesn't duplicate free blocks */ + } + } + +:mps:tag:`usage.delete` Client function to allocate the first block of a +given size in address order. For simplicity, this allocates the entire +block:: + + Bool FreeTreeAllocate(Addr *baseReturn, Size *sizeReturn, + FreeTree tree, Size size) { + SplayTree splayTree = &tree->splayTree; + SplayNode splayNode; + Bool found; + + /* look for the first node of at least the given size. */ + /* closureP parameter is not used. See `.function.splay.find.first.`_ */ + found = SplayFindFirst(&splayNode, splayTree, + FreeBlockTestNode, FreeBlockTestTree, + NULL, (unsigned long)size); + + if (found) { + FreeBlock freeNode = FreeBlockOfSplayNode(splayNode); + Void *key = (void *)freeNode->base; /* use base of block as the key */ + Res res; + + /* allocate the block */ + *baseReturn = freeNode->base; + *sizeReturn = freeNode->size; + + /* remove the node from the splay tree - :mps:ref:`.function.splay.tree.delete` */ + res = SplayTreeDelete(splayTree, splayNode, key); + AVER(res == ResOK); /* Must be possible to delete node */ + + /* Delete the block */ + deallocate(freeNode, (sizeof(FreeBlockStruct)); + + return TRUE; + + } else { + /* No suitable block */ + return FALSE; + } + } + + +Implementation +-------------- + +:mps:tag:`impl` For more details of how splay trees work, see paper.st85(0). +For more details of how to implement operations on splay trees, see +paper.sleator96(0). Here we describe the operations involved. + + +Top-down splaying +................. + +:mps:tag:`impl.top-down` The method chosen to implement the splaying +operation is called "top-down splay". This is described as "procedure +top-down splay" in paper.st85(0) - although the implementation here +additionally permits attempts to access items which are not known to +be in the tree. Top-down splaying is particularly efficient for the +common case where the location of the node in a tree is not known at +the start of an operation. Tree restructuring happens as the tree is +descended, whilst looking for the node. + +:mps:tag:`impl.splay` The key to the operation of the splay tree is the +internal function :c:func:`SplaySplay()`. It searches the tree for a node +with a given key and returns whether it suceeded. In the process, it +brings the found node, or an arbitrary neighbour if not found, to the +root of the tree. This "bring-to-root" operation is performed top-down +during the search, and it is not the simplest possible bring-to-root +operation, but the resulting tree is well-balanced, and will give good +amortised cost for future calls to :c:func:`SplaySplay()`. (See +paper.st85(0)) + +:mps:tag:`impl.splay.how` To perform this top-down splay, the tree is broken +into three parts, a left tree, a middle tree and a right tree. We +store the left tree and right tree in the right and left children +respectively of a "sides" node to eliminate some boundary conditions. +The initial condition is that the middle tree is the entire splay +tree, and the left and right trees are empty. We also keep pointers to +the last node in the left tree, and the first node in the right tree. +Note that, at all times, the three trees are each validly ordered, and +they form a partition with the ordering left, middle, right. The splay +is then performed by comparing the middle tree with the following six +cases, and performing the indicated operations, until none apply. + +:mps:tag:`impl.splay.cases` Note that paper.st85(0)(Fig. 3) describes only 3 +cases: zig, zig-zig and zig-zag. The additional cases described here +are the symmetric variants which are respectively called zag, zag-zag +and zag-zig. In the descriptions of these cases, ``root`` is the root +of the middle tree; ``node->left`` is the left child of ``node``; +``node->right`` is the right child of ``node``. The comparison +operators (``<``, ``>``, ``==``) are defined to compare a key and a +node in the obvious way by comparing the supplied key with the node's +associated key. + +:mps:tag:`impl.splay.zig` The "zig" case is where ``key < root``, and either: + +- ``key == root->left``; +- ``key < root->left && root->left->left == NULL``; or +- ``key > root->left && root->left->right == NULL``. + +The operation for the zig case is: link right (see :mps:ref:`.impl.link.right`). + +:mps:tag:`impl.splay.zag` The "zag" case is where ``key > root``, and either: + +- ``key == root->right``; +- ``key < root->right && root->right->left == NULL``; or +- ``key > root->right && root->right->right == NULL``. + +The operation for the zag case is: link left (see :mps:ref:`.impl.link.left`). + +:mps:tag:`impl.splay.zig.zig` The "zig-zig" case is where + +- ``key < root && key < root->left && root->left->left != NULL``. + +The operation for the zig-zig case is: rotate right (see +:mps:ref:`.impl.rotate.right`) followed by link right (see +:mps:ref:`.impl.link.right`). + +:mps:tag:`impl.splay.zig.zag` The "zig-zag" case is where + +- ``key < root && key > root->left && root->left->right != NULL``. + +The operation for the zig-zag case is: link right (see +:mps:ref:`.impl.link.right`) followed by link left (see :mps:ref:`.impl.link.left`). + +:mps:tag:`impl.splay.zag.zig` The "zag-zig" case is where + +- ``key > root && key < root->right && root->right->left != NULL``. + +The operation for the zag-zig case is: link left (see +:mps:ref:`.impl.link.left`) followed by link right (see :mps:ref:`.impl.link.right`). + +:mps:tag:`impl.splay.zag.zag` The "zag-zag" case is where + +- ``key > root && key > root->right && root->right->right != NULL``. + +The operation for the zag-zag case is: rotate left (see +:mps:ref:`.impl.rotate.left`) followed by link left (see :mps:ref:`.impl.link.left`). + +:mps:tag:`impl.splay.terminal.null` A special terminal case is when + +- ``root == NULL``. + +This can only happen at the beginning, and cannot arise from the +operations above. In this case, the splay operation must return +:c:macro:`NULL`, and "not found". + +:mps:tag:`impl.splay.terminal.found` One typical terminal case is when + +- ``key == root``. + +This case is tested for at the beginning, in which case "found" is +returned immediately. If this case happens as a result of other +operations, the splay operation is complete, the three trees are +assembled (see :mps:ref:`.impl.assemble`), and "found" is returned. + +:mps:tag:`impl.splay.terminal.not-found` The other typical terminal cases are: + +- ``key < root && root->left == NULL``; and +- ``key > root && root->right == NULL``. + +In these cases, the splay operation is complete, the three trees are assembled +(see :mps:ref:`.impl.assemble`), and "not found" is returned. + +:mps:tag:`impl.rotate.left` The "rotate left" operation (see paper.st85(0) +Fig. 1) rearranges the middle tree as follows (where any of sub-trees +A, B and C may be empty): + +[missing diagram] + +:mps:tag:`impl.rotate.right` The "rotate right" operation (see paper.st85(0) +Fig. 1) rearranges the middle tree as follows (where any of sub-trees +A, B and C may be empty): + +[missing diagram] + +:mps:tag:`impl.link.left` The "link left" operation (see paper.st85(0) Fig. +11a for symmetric variant) rearranges the left and middle trees as +follows (where any of sub-trees A, B, L and R may be empty): + +[missing diagram] + +The last node of the left tree is now x. + +:mps:tag:`impl.link.right` The "link right" operation (see paper.st85(0) +Fig. 11a) rearranges the middle and right trees as follows (where any +of sub-trees A, B, L and R may be empty): + +[missing diagram] + +The first node of the right tree is now x. + +:mps:tag:`impl.assemble` The "assemble" operation (see paper.st85(0) +Fig. 12) merges the left and right trees with the middle tree as +follows (where any of sub-trees A, B, L and R may be empty): + +[missing diagram] + + +Top-level operations +.................... + +:mps:tag:`impl.insert` :c:func:`SplayTreeInsert()`: (See paper.sleator96(0), chapter +4, function insert). If the tree has no nodes, [how does it smell?] +add the inserted node and we're done; otherwise splay the tree around +the supplied key. If the splay successfully found a matching node, +return failure. Otherwise, add the inserted node as a new root, with +the old (newly splayed, but non-matching) root as its left or right +child as appropriate, and the opposite child of the old root as the +other child of the new root. + +:mps:tag:`impl.delete` :c:func:`SplayTreeDelete()`: (See paper.sleator96(0), chapter +4, function delete). Splay the tree around the supplied key. Check +that the newly splayed root is the same node as given by the caller, +and that it matches the key; return failure if not. If the given node +(now at the root) has fewer than two children, replace it (as root), +with the non-null child or null. Otherwise, set the root of the tree +to be the left child (arbitrarily) of the node to be deleted, and +splay around the same key. The new root will be the last node in the +sub-tree and will have a null right child; this is set to be the right +child of the node to be deleted. + +:mps:tag:`impl.search` :c:func:`SplayTreeSearch()`: Splay the node around the +supplied key. If the splay found a matching node, return it; otherwise +return failure. + +:mps:tag:`impl.neighbours` :c:func:`SplayTreeNeighbours()`: Splay the tree around +the supplied key. If the splay found a matching node, return failure. +Otherwise, determine whether the (non-matching) found node is the left +or right neighbour of the key (by comparison with the key). Set the +tree root to be the right or left child of that first neighbour +respectively, and again splay the tree around the supplied key. The +new root will be the second neighbour, and will have a null left or +right child respectively. Set this null child to be the first +neighbour. Return the two neighbours. + +:mps:tag:`impl.neighbours.note` Note that it would be possible to implement +:c:func:`SplayTreeNeighbours()` with only one splay, and then a normal +binary tree search for the left or right neighbour of the root. This +would be a cheaper operation, but would give poorer amortised cost if +the call to :c:func:`SplayTreeNeighbours()` typically precedes a call to +:c:func:`SplayTreeInsert()` (which is expected to be a common usage +pattern - see :mps:ref:`.usage.insert`). It's also possible to implement +:c:func:`SplayTreeNeighbours()` by simply keeping track of both neighbours +during a single splay. This has about the same cost as a single splay, +and hence about the same amortised cost if the call to +:c:func:`SplayTreeNeighbours()` typically precedes a call to +:c:func:`SplayTreeInsert()`. + +:mps:tag:`impl.next` :c:func:`SplayTreeNext()`: Splay the tree around the supplied +``oldKey``. During iteration the "old node" found is probably already +at the root, in which case this will be a null operation with little +cost. If this old node has no right child, return :c:macro:`NULL`. Otherwise, +split the tree into a right tree (which contains just the right child +of the old node) and a left tree (which contains the old node, its +left child and no right child). The next node is the first node in the +right tree. Find this by splaying the right tree around ``oldKey`` +(which is known to compare ``CompareLESS`` than any keys in the right +tree). Rejoin the full tree, using the right tree as the root and +setting the left child of root to be the left tree. Return the root of +this tree. + + +Testing +------- + +:mps:tag:`test` There is no plan to test splay trees directly. It is +believed that the testing described in design.mps.cbs.test will be +sufficient to test this implementation. + + +Error Handling +-------------- + +:mps:tag:`error` This module detects and reports most common classes of +protocol error. The cases it doesn't handle will result in undefined +behaviour and probably cause an :c:macro:`AVER` to fire. These are: + +:mps:tag:`error.bad-pointer` Passing an invalid pointer in place of a +:c:type:`SplayTree` or :c:type:`SplayNode`. + +:mps:tag:`error.bad-compare` Initialising a :c:type:`SplayTree` with a compare +function that is not a valid compare function, or which doesn't +implement a total ordering on splay nodes. + +:mps:tag:`error.bad-describe` Passing an invalid describe method to +:c:func:`SplayTreeDescribe()`. + +:mps:tag:`error.out-of-stack` Stack exhaustion under :c:func:`SplayTreeDescribe()`. + + +Future +------ + +:mps:tag:`future.tree` It would be possible to split the splay tree module +into two: one that implements binary trees; and one that implements +splay trees on top of a binary tree. + +:mps:tag:`future.parent` The iterator could be made more efficient (in an +amortized sense) if it didn't splay at each node. To implement this +(whilst meeting :mps:ref:`.req.stack`) we really need parent pointers from the +nodes. We could use the (first-child, right-sibling/parent) trick +described in paper.st85 to implement this, at a slight cost to all +other tree operations, and an increase in code complexity. paper.st85 +doesn't describe how to distinguish the first-child between left-child +and right-child, and the right-sibling/parent between right-sibling +and parent. One could either use the comparator to make these +distinctions, or steal some bits from the pointers. + +:mps:tag:`future.reverse` The assembly phase could be made more efficient if +the link left and link right operations were modified to add to the +left and right trees with pointers reversed. This would remove the +need for the assembly phase to reverse them. + + diff --git a/mps/manual/html/_sources/design/sso1al.txt b/mps/manual/html/_sources/design/sso1al.txt new file mode 100644 index 00000000000..bad65430022 --- /dev/null +++ b/mps/manual/html/_sources/design/sso1al.txt @@ -0,0 +1,131 @@ +.. _design-sso1al: + + +.. index:: + pair: Digital Unix on Alpha stack scanner; design + pair: Digital Unix on Alpha; stack scanner design + + +Stack scanner for Digital Unix on Alpha +======================================= + +.. mps:prefix:: design.mps.sso1al + pair: Digital Unix on Alpha stack scanner; design + pair: Digital Unix on Alpha; stack scanner design + +.. warning:: + + As of 2013-05-26, the MPS is no longer supported on Digital Unix, + so this document is only of historical interest. + + +Introduction +------------ + +:mps:tag:`readership` Any MPS developer. + +:mps:tag:`intro` This is the design for Stack Scanner module that runs on +Digital UNIX / Alpha systems (See os.o1 and arch.al). The design +adheres to the general design and interface described (probably not +described actually) in design.mps.ss. + +:mps:tag:`source.alpha` book.digital96 (Alpha Architecture Handbook) +describes the Alpha Architecture independently of any particular +implementation. The instruction mnemonics and the semantics for each +instruction are specified in that document. + +[DEC_Assembler]_ describes the assembler syntax and assembler +directives. It also summarises the calling conventions used. Chapters +1 and 6 were especially useful, especially chapter 6. + +[DEC_Alpha_Calling_Standard]_ describes the calling conventions used +for Digital Alpha systems. Chapter 2 was useful. But the whole +document was not used as much as the previous 2 documents. + + +Definitions +----------- + +:mps:tag:`def.saved` Saved Register. A saved register is one whose value is defined to +be preserved across a procedure call according to the Calling Standard. They +are ``$9``--``$15``, ``$26``, and ``$30``. ``$30`` is the stack pointer. + +:mps:tag:`def.non-saved` Non-Saved Register. A non-save register is a +register that is assumed to be modified across a procedure call +according to the Calling Standard. + +:mps:tag:`def.tos` Top of Stack. The top of stack is the youngest portion of +the stack. + +:mps:tag:`def.bos` Bottom of Stack. The bottom of stack is the oldest +portion of the stack. + +:mps:tag:`def.base` Base. Of a range of addresses, the base is the lowest +address in the range. + +:mps:tag:`def.limit` Limit. Of a range of addresses, the limit is "one past" +the highest address in the range. + + +Overview +-------- + +:mps:tag:`overview` The registers and the stack need to be scanned. This is +achieved by storing the contents of the registers into a frame at the +top of the stack and then passing the base and limit of the stack +region, including the newly created frame, to the function +:c:func:`TraceScanAreaTagged()`. :c:func:`TraceScanAreaTagged()` performs the +actual scanning and fixing. + + +Detail Design +------------- + +Functions +......... + +:mps:tag:`fun.stackscan` :c:func:`StackScan()` + +:mps:tag:`fun.stackscan.asm` The function is written in assembler. +:mps:tag:`fun.stackscan.asm.justify` This is because the machine registers +need to be examined, and it is only possible to access the machine +registers using assembler. + +:mps:tag:`fun.stackscan.entry` On entry to this procedure all the non-saved +(temporary) registers that contain live pointers must have been saved +in some root (usually the stack) by the mutator (otherwise it would +lose the values). Therefore only the saved registers need to be stored +by this procedure. + +:mps:tag:`fun.stackscan.assume.saved` We assume that all the saved registers +are roots. This is conservative since some of the saved registers +might not be used. + +:mps:tag:`fun.stackscan.frame` A frame is be created on the top of the +stack. :mps:tag:`fun.stackscan.frame.justify` This frame is used to store +the saved registers into so that they can be scanned. + +:mps:tag:`fun.stackscan.save` All the saved registers, apart from $30 the +stack pointer, are to be stored in the frame. +:mps:tag:`fun.stackscan.save.justify` This is so that they can be scanned. +The stack pointer itself is not scanned as the stack is assumed to be +a root (and therefore a priori alive). + +:mps:tag:`fun.stackscan.call` :c:func:`TraceScanAreaTagged()` is called with the +current stack pointer as the base and the (passed in) ``StackBot`` as +the limit of the region to be scanned. :mps:tag:`fun.stackscan.call.justify` +This function does the actual scanning. The Stack on Alpha systems +grows down so the stack pointer (which points to the top of the stack) +is lower in memory than the bottom of the stack. + +:mps:tag:`fun.stackscan.return` The return value from +:c:func:`TraceScanAreaTagged()` is used as the return value for +:c:func:`StackScan()`. + + +References +---------- + +.. [DEC_Assembler] Digital Equipment Corporation. 1996; . .. [DEC_Alpha_Calling_Standard] "Calling Standard for Alpha Systems"; Digital Equipment Corporation. 1996. "`Assembly Language Programmer's Guide `__". + + diff --git a/mps/manual/html/_sources/design/telemetry.txt b/mps/manual/html/_sources/design/telemetry.txt index 58b119aff6b..e9e682896ac 100644 --- a/mps/manual/html/_sources/design/telemetry.txt +++ b/mps/manual/html/_sources/design/telemetry.txt @@ -1,6 +1,434 @@ +.. _design-telemetry: + + .. index:: pair: telemetry; design -.. _design-telemetry: -.. include:: ../../converted/telemetry.rst +Telemetry +========= + +.. mps:prefix:: design.mps.telemetry + + +Introduction +------------ + +:mps:tag:`intro` This documents the design of the telemetry mechanism within +the MPS. + +:mps:tag:`readership` This document is intended for any MPS developer. + +:mps:tag:`source` Various meetings and brainstorms, including +meeting.general.1997-03-04(0), mail.richard.1997-07-03.17-01(0), +mail.gavinm.1997-05-01.12-40(0). + + +Overview +-------- + +:mps:tag:`over` Telemetry permits the emission of events from the MPS. These +can be used to drive a graphical tool, or to debug, or whatever. The +system is flexible and robust, but doesn't require heavy support from +the client. + + +Requirements +------------ + +:mps:tag:`req.simple` It must be possible to generate code both for the MPS +and any tool without using complicated build tools. + +:mps:tag:`req.open` We must not constrain the nature of events before we are +certain of what we want them to be. + +:mps:tag:`req.multi` We must be able to send events to multiple streams. + +:mps:tag:`req.share` It must be possible to share event descriptions between +the MPS and any tool. + +:mps:tag:`req.version` It must be possible to version the set of events so +that any tool can detect whether it can understand the MPS. + +:mps:tag:`req.back` Tools should be able to understand older and newer +version of the MPS, so far as is appropriate. + +:mps:tag:`req.type` It must be possible to transmit a rich variety of types +to the tool, including doubles, and strings. + +:mps:tag:`req.port` It must be possible to transmit and receive events +between different platforms. + +:mps:tag:`req.control` It must be possible to control whether and what +events are transmitted at least at a coarse level. + +:mps:tag:`req.examine` There should be a cheap means to examine the contents +of logs. + +:mps:tag:`req.pm` The event mechanism should provide for post mortem to +detect what significant events led up to death. + +:mps:tag:`req.perf` Events should not have a significant effect on +performance when unwanted. + +:mps:tag:`req.small` Telemetry streams should be small. + +:mps:tag:`req.avail` Events should be available in all varieties, subject to +performance requirements. + +:mps:tag:`req.impl` The plinth support for telemetry should be easy to write +and flexible. + +:mps:tag:`req.robust` The telemetry protocol should be robust against some +forms of corruption, e.g. packet loss. + +:mps:tag:`req.intern` It should be possible to support string-interning. + + +Architecture +------------ + +:mps:tag:`arch` Event annotations are scattered throughout the code, but +there is a central registration of event types and properties. Events +are written to a buffer via a specialist structure, and are optionally +written to the plinth. Events can take any number of parameters of a +range of types, indicated as a format both in the annotation and the +the registry. + + +Analysis +-------- + +:mps:tag:`anal` The proposed order of development, with summary of +requirements impact is as follows (★ for positive impact, ⇓ for +negative impact): + +========================= == == == == == == == == == == == == == == == == == ======= +solution si op mu sh ve ty po co ex pm pe sm av im ro in ba status +========================= == == == == == == == == == == == == == == == == == ======= +:mps:ref:`.sol.format` · · · · · ★ · · · · · · · · · · · merged +:mps:ref:`.sol.struct` · · · · · ★ · · · · ★ ⇓ · · · · · merged +:mps:ref:`.sol.string` · · · · · ★ · · · · · · · · · ★ · merged +:mps:ref:`.sol.relation` ★ · · ★ · · · · ★ · · ★ · · · · · merged +:mps:ref:`.sol.dumper` · · · · · · · · ★ · · · · · · · · merged +:mps:ref:`.sol.kind` · ⇓ · · · · · ★ · ★ · · · · · · · merged +:mps:ref:`.sol.control` · · · · · · · ★ · · ★ · · · · · · merged +:mps:ref:`.sol.variety` · · · · · · · · · ★ ★ · ★ · · · · +========================= == == == == == == == == == == == == == == == == == ======= + +The following are not yet ordered: + +========================= == == == == == == == == == == == == == == == == == ======= +solution si op mu sh ve ty po co ex pm pe sm av im ro in ba status +========================= == == == == == == == == == == == == == == == == == ======= +:mps:ref:`.sol.buffer` · · · · · · · ★ · ★ ★ · · · · · . +:mps:ref:`.sol.traceback` · · · · · · · · · ★ · · · · · · . +:mps:ref:`.sol.client` · · · · · · · · · · · · · · · ★ . +:mps:ref:`.sol.head` · · · · · · ★ · · · · · · · · · . +:mps:ref:`.sol.version` · · · · ★ · · · · · · · · · · · ★ +:mps:ref:`.sol.exit` · · · · · · · · · ★ · · · · · · . +:mps:ref:`.sol.block` · · · · · · · · · · ★ ⇓ · · ★ · · +:mps:ref:`.sol.code` · · · · · · · · · · · ★ · · · · ★ +:mps:ref:`.sol.msg` · · ★ · · · ★ · · · · · · ★ ★ · . +========================= == == == == == == == == == == == == == == == == == ======= + +:mps:tag:`file-format` One of the objectives of this plan is to minimise the +impact of the changes to the log file format. This is to be achieved +firstly by completing all necessary support before changes are +initiated, and secondly by performing all changes at the same time. + + +Ideas +----- + +:mps:tag:`sol.format` Event annotations indicate the types of their +arguments, for example, :c:macro:`EVENT_WD` for a :c:type:`Word` and a ``double``. +(:mps:ref:`.req.type`) + +:mps:tag:`sol.struct` Copy event data into a structure of the appropriate +type, for example, :c:type:`EventWDStruct`. (:mps:ref:`.req.type`, :mps:ref:`.req.perf`, but +not :mps:ref:`.req.small` because of padding) + +:mps:tag:`sol.string` Permit at most one string per event, at the end, and +use the ``char[1]`` hack, and specialised code; deduce the string +length from the event length and also :c:macro:`NUL`-terminate (:mps:ref:`.req.type`, +:mps:ref:`.req.intern`) + +:mps:tag:`sol.buffer` Enter all events initially into internal buffers, and +conditionally send them to the message stream. (:mps:ref:`.req.pm`, +:mps:ref:`.req.control`, :mps:ref:`.req.perf`) + +:mps:tag:`sol.variety` In optimized varieties, have internal events (see +:mps:ref:`.sol.buffer`) for a subset of events and no external events; in +normal varieties have all internal events, and the potential for +external events. (:mps:ref:`.req.avail`, :mps:ref:`.req.pm`, :mps:ref:`.req.perf`) + +:mps:tag:`sol.kind` Divide events by some coarse type into around 6 groups, +probably related to frequency. (:mps:ref:`.req.control`, :mps:ref:`.req.pm`, but not +:mps:ref:`.req.open`) + +:mps:tag:`sol.control` Hold flags to determine which events are emitted +externally. (:mps:ref:`.req.control`, :mps:ref:`.req.perf`) + +:mps:tag:`sol.dumper` Write a simple tool to dump event logs as text. +(:mps:ref:`.req.examine`) + +:mps:tag:`sol.msg` Redesign the plinth interface to send and receive +messages, based on any underlying IPC mechanism, for example, append +to file, TCP/IP, messages, shared memory. (:mps:ref:`.req.robust`, +:mps:ref:`.req.impl`, :mps:ref:`.req.port`, :mps:ref:`.req.multi`) + +:mps:tag:`sol.block` Buffer the events and send them as fixed size blocks, +commencing with a timestamp, and ending with padding. (:mps:ref:`.req.robust`, +:mps:ref:`.req.perf`, but not :mps:ref:`.req.small`) + +:mps:tag:`sol.code` Commence each event with two bytes of event code, and +two bytes of length. (:mps:ref:`.req.small`, :mps:ref:`.req.back`) + +:mps:tag:`sol.head` Commence each event stream with a platform-independent +header block giving information about the session, version (see +:mps:ref:`.sol.version`), and file format; file format will be sufficient to +decode the (platform-dependent) rest of the file. (:mps:ref:`.req.port`) + +:mps:tag:`sol.exit` Provide a mechanism to flush events in the event of +graceful sudden death. (:mps:ref:`.req.pm`) + +:mps:tag:`sol.version` Maintain a three part version number for the file +comprising major (incremented when the format of the entire file +changes (other than platform differences)), median (incremented when +an existing event changes its form or semantics), and minor +(incremented when a new event type is added); tools should normally +fail when the median or major is unsupported. (:mps:ref:`.req.version`, +:mps:ref:`.req.back`) + +:mps:tag:`sol.relation` Event types will be defined in terms of a relation +specifying their name, code, optimised behaviour (see +:mps:ref:`.sol.variety`), kind (see :mps:ref:`.sol.kind`), and format (see +:mps:ref:`.sol.format`); both the MPS and tool can use this by suitable +``#define`` hacks. (:mps:ref:`.req.simple`, :mps:ref:`.req.share`, :mps:ref:`.req.examine`, +:mps:ref:`.req.small` (no format information in messages)) + +:mps:tag:`sol.traceback` Provide a mechanism to output recent events (see +:mps:ref:`.sol.buffer`) as a form of backtrace when :c:macro:`AVER` statements fire +or from a debugger, or whatever. (:mps:ref:`.req.pm`) + +:mps:tag:`sol.client` Provide a mechanism for user events. (:mps:ref:`.req.intern`) + + + +Implementation +-------------- + +Annotation +.......... + +:mps:tag:`annot` An event annotation is of the form:: + + EVENT3(FooCreate, pointer, address, word) + +:mps:tag:`annot.string` If there is a string in the format, it must be the +last parameter (and hence there can be only one). There is currrently +a maximum string length, defined by ``EventMaxStringLength`` in +impl.h.eventcom. + +:mps:tag:`annot.type` The event type should be given as the first parameter +to the event macro, as registered in impl.h.eventdef. + +:mps:tag:`annot.param` The parameters of the event should be given as the +remaining parameters of the event macro, in order as indicated in the +event parameters definition in impl.h.eventdef. + + +Registration +............ + +:mps:tag:`reg` All event types and parameters should be registered in +impl.h.eventdef, in the form of a higher-order list macros. + +:mps:tag:`reg.just` This use of a higher-order macros enables great +flexibility in the use of this file. + +:mps:tag:`reg.rel` The event type registration is of the form:: + + EVENT(X, FooCreate, 0x1234, TRUE, Arena) + +:mps:tag:`reg.type` The first parameter of the relation is the event type. +This needs no prefix, and should correspond to that used in the +annotation. + +:mps:tag:`reg.code` The second parameter is the event code, a 16-bit value +used to represent this event type. Codes should not be re-used for new +event types, to allow interpretation of event log files of all ages. + +:mps:tag:`reg.always` The third parameter is a boolean value indicating +whether this event type should be implemented in all varieties. See +:mps:ref:`.control.buffer`. Unless your event is on the critical path +(typically per reference or per object), you will want this to be +:c:macro:`TRUE`. + +:mps:tag:`reg.kind` The fourth parameter is a kind keyword indicating what +category this event falls into. See :mps:ref:`.control`. The possible values +are: + +- :c:type:`Arena` -- per space or arena or global +- :c:type:`Pool` -- pool-related +- :c:type:`Trace` -- per trace or scan +- :c:type:`Seg` -- per segment +- :c:type:`Ref` -- per reference or fix +- ``Object`` -- per object or allocation +- ``User`` -- invoked by the user through the MPS interface + +This list can be seen in impl.h.eventcom. + +:mps:tag:`reg.doc` Add a docstring column. [RB 2012-09-03] + +:mps:tag:`reg.params` The event parameters registration is of the form:: + + #define EVENT_FooCreate_PARAMS(PARAM, X) \ + PARAM(X, 0, P, firstParamPointer) \ + PARAM(X, 1, U, secondParamUnsigned) + +:mps:tag:`reg.param.index` The first column is the index, and must start at +zero and increase by one for each row. + +:mps:tag:`reg.param.sort` The second column is the parameter "sort", which, +when appended to ``EventF``, yields a type for the parameter. It is a +letter from the following list: + +- ``P`` -- ``void *`` +- ``A`` -- :c:type:`Addr` +- ``W`` -- :c:type:`Word` +- ``U`` -- ``unsigned int`` +- ``S`` -- ``char *`` +- ``D`` -- ``double`` +- ``B`` -- :c:type:`Bool` + +The corresponding event parameter must be assignment compatible with +the type. + +:mps:tag:`param.types` When an event has parameters whose type is not in the +above list, use the following guidelines: All ``C`` pointer types not +representing strings use ``P``; :c:type:`Size`, :c:type:`Count`, :c:type:`Index` use +``W``; others should be obvious. + +:mps:tag:`reg.param.name` The third column is the parameter name. It should +be a valid C identifier and is used for debugging display and human +readable output. + +:mps:tag:`reg.param.doc` Add a docstring column. [RB 2012-09-03] + +:mps:tag:`reg.dup` It is permissible for the one event type to be used for +more than one annotation. There are generally two reasons for this: + +- Variable control flow for successful function completion; +- Platform/Otherwise-dependent implementations of a function. + +Note that all annotations for one event type must have the same format +(as implied by :mps:ref:`.sol.format`). + + +Control +....... + +:mps:tag:`control` There are two types of event control, buffer and output. + +:mps:tag:`control.buffer` Buffer control affects whether particular events +implemented at all, and is controlled statically by variety using the +always value (see :mps:ref:`.reg.always`) for the event type. The hot variety +does compiles out annotations with ``always=FALSE``. The cool variety +does not, so always buffers a complete set of events. + +:mps:tag:`control.output` Output control affects whether events written to +the internal buffer are output via the plinth. This is set on a +per-kind basis (see :mps:ref:`.reg.kind`), using a control bit table stored in +EventKindControl. By default, all event kinds are off. You may switch +some kinds on using a debugger. + +For example, to enable :c:type:`Pool` events using gdb (see impl.h.eventcom for +numeric codes):: + + $ gdb ./xci3gc/cool/amcss + (gdb) break GlobalsInit + (gdb) run + ... + (gdb) print EventKindControl |= 2 + $2 = 2 + (gdb) continue + ... + (gdb) quit + $ mpseventcnv -v | sort | head + 0000178EA03ACF6D PoolInit 9C1E0 9C000 0005E040 + 0000178EA03C2825 PoolInitMFS 9C0D8 9C000 1000 C + 0000178EA03C2C27 PoolInitMFS 9C14C 9C000 1000 44 + 0000178EA03C332C PoolInitMV 9C080 9C000 1000 20 10000 + 0000178EA03F4DB4 BufferInit 2FE2C4 2FE1B0 0 + 0000178EA03F4EC8 BufferInitSeg 2FE2C4 2FE1B0 0 + 0000178EA03F57DA AMCGenCreate 2FE1B0 2FE288 + 0000178EA03F67B5 BufferInit 2FE374 2FE1B0 0 + 0000178EA03F6827 BufferInitSeg 2FE374 2FE1B0 0 + 0000178EA03F6B72 AMCGenCreate 2FE1B0 2FE338 + +:mps:tag:`control.env` The initial value of ``EventKindControl`` is read +from the C environment when the ANSI Plinth is used, and so event +output can be controlled like this:: + + MPS_TELEMETRY_CONTROL=127 amcss + +or like this:: + + MPS_TELEMETRY_CONTROL="Pool Arena" amcss + +where the variable is set to a space-separated list of names defined by ``EventKindENUM``. + +:mps:tag:`control.just` These controls are coarse, but very cheap. + +:mps:tag:`control.external` The MPS interface function +``mps_telemetry_control`` can be used to change ``EventKindControl``. + +:mps:tag:`control.tool` The tools will be able to control +``EventKindControl``. + + +Debugging +......... + +:mps:tag:`debug.buffer` Each event kind is logged in a separate buffer, +``EventBuffer[kind]``. + +:mps:tag:`debug.buffer.reverse` The events are logged in reverse order from +the top of the buffer, with the last logged event at +``EventLast[kind]``. This allows recovery of the list of recent events +using the ``event->any.size`` field. + +:mps:tag:`debug.dump` The contents of all buffers can be dumped with the +``EventDump`` function from a debugger, for example:: + + gdb> print EventDump(mps_lib_get_stdout()) + +:mps:tag:`debug.describe` Individual events can be described with the +EventDescribe function, for example:: + + gdb> print EventDescribe(EventLast[3], mps_lib_get_stdout()) + +:mps:tag:`debug.core` The event buffers are preserved in core dumps and can +be used to work out what the MPS was doing before a crash. Since the +kinds correspond to frequencies, ancient events may still be available +in some buffers, even if they have been flushed to the output stream. +Some digging may be required. + + +Dumper tool +........... + +:mps:tag:`dumper` A primitive dumper tool is available in impl.c.eventcnv. +For details, see guide.mps.telemetry. + + +Allocation replayer tool +........................ + +:mps:tag:`replayer` A tool for replaying an allocation sequence from a log +is available in impl.c.replay. For details, see +design.mps.telemetry.replayer. + + diff --git a/mps/manual/html/_sources/design/tests.txt b/mps/manual/html/_sources/design/tests.txt new file mode 100644 index 00000000000..2d6ab4addca --- /dev/null +++ b/mps/manual/html/_sources/design/tests.txt @@ -0,0 +1,53 @@ +.. _design-tests: + + +Tests +===== + +.. mps:prefix:: design.mps.tests + + +Introduction +------------ + +:mps:tag:`intro` This document contains a guide to the Memory Pool System +tests. + +:mps:tag:`readership` This document is intended for any MPS developer. + + +Smoke tests +----------- + +:mps:tag:`smoke` The "smoke tests" provide quick checks that the MPS is +working. They run quickly enough for it to be practical to run them +every time the MPS is built. + +:mps:tag:`randomize` Each time a test case is run, it randomly chooses some +of its parameters (for example, the sizes of objects, or how many +links to create in a graph of references). This allows a fast test +to cover many cases over time. + +:mps:tag:`randomize.repeatable` The random numbers are chosen +pseudo-randomly based on a seed initialized from environmental data +(the time and the processor cycle count). The seed is reported at test +startup. Each test can be run with a specified seed. This ensures +that the deterministic tests are repeatable. + + +Smoke test list +............... + +:mps:tag:`test.finalcv` Registers objects for finalization, makes them +unreachable, deregisters them, etc. Churns to provoke minor (nursery) +collection. + +:mps:tag:`test.finaltest` Creates a large binary tree, and registers every +node. Drops the top reference, requests collection, and counts the +finalization messages. + +:mps:tag:`test.zcoll` Collection scheduling, and collection feedback. + +:mps:tag:`test.zmess` Message lifecycle and finalization messages. + + diff --git a/mps/manual/html/_sources/design/thread-manager.txt b/mps/manual/html/_sources/design/thread-manager.txt new file mode 100644 index 00000000000..4392775d217 --- /dev/null +++ b/mps/manual/html/_sources/design/thread-manager.txt @@ -0,0 +1,155 @@ +.. _design-thread-manager: + + +.. index:: + pair: thread manager; design + + +Thread Manager +============== + +.. mps:prefix:: design.mps.thread-manager + + +Purpose +------- + +The Thread Manager handles various thread-related functions required +by the MPS. These are: + +- stack scanning; +- suspension and resumption of the mutator threads. + + +Context +------- + +The barrier requires suspension and resumption of threads in order to +ensure that the collector has exclusive access to part of memory. +[design.mps.barrier.@@@@] + +Stack scanning is provided as a service to the client. [Link?@@@@] + + +Overview +-------- + +.. c:type:: struct mps_thr_s *Thread + +Each thread is represented by an object of type :c:type:`Thread`. The +:c:type:`Thread` type is implemented as an ADT. A list of :c:type:`Thread` objects +is maintained in the arena (as the :c:type:`Ring` structure +``arena->threadRing``). The :c:type:`Thread` object contains +operating-system-dependent information about the thread -- information +necessary for manipulating the thread and for scanning the thread +context. Thread "registration" adds or removes the current thread to +the :c:type:`Thread` list in the arena. + + +Detailed Design +--------------- + +Stack scan +.......... + +This is a module providing a stack scanning function. The scanning is +architecture and operating system dependent. Typically the function +will push the subste of the save registers (those preserved across +function calls) which may contain pointers (that is, neither +floating-point or debugging registers) and call :c:func:`TraceScanStack()` +on the appropriate range. + + +Thread interface +................ + +.. c:function:: Res ThreadRegister(Thread *threadReturn, Arena arena) + +Register the current thread with the arena, allocating a new +``*threadReturn`` to point to it. + +.. c:function:: void ThreadDeregister(Thread thread, Arena arena) + +Remove ``thread`` from the list of threads managed by the arena and +free it. + +.. c:function:: void ThreadRingSuspend(Ring threadRing) + +Suspend all the threads on the list ``threadRing``, except for the +current thread. + +.. c:function:: void ThreadRingResume(Ring threadRing) + +Resume all the threads on the list ``threadRing``. + +.. c:function:: Res ThreadScan(ScanState ss, Thread thread, void *stackBot) + +Scan the stacks and root registers of ``thread``, treating each value +found as an ambiguous reference. The exact definition is operating +system and architecture dependent + + +Single-threaded generic implementation +...................................... + +In ``than.c``. + +- Single threaded (calling :c:func:`ThreadRegister()` on a second thread + causes an assertion failure). +- :c:func:`ThreadRingSuspend()` and :c:func:`ThreadRingResume()` do nothing + because there are no other threads. +- :c:func:`ThreadScan()` calls :c:func:`StackScan()`. + + +POSIX threads implementation +............................ + +In ``thix.c``. See design.mps.pthreadext. + + +Win32 implementation +.................... + +In ``thw3.c``. + +- Supports multiple threads. +- Structured exception style faults are expected. +- :c:func:`ThreadRingSuspend()` and :c:func:`ThreadRingResume()` loop over threads + and call Win32 API functions :c:func:`SuspendThread()` and + :c:func:`ResumeThread()`. +- :c:func:`ThreadRegister()` records information for the current thread: + + - A Win32 "handle" with access flags :c:macro:`THREAD_SUSPEND_RESUME` and + :c:macro:`THREAD_GET_CONTEXT`. This handle is needed as parameter to + :c:func:`SuspendThread()` and :c:func:`ResumeThread()`. + - The Win32 :c:func:`GetCurrentThreadId()` so that the current thread may + be identified. + +- Stack scanning is Win32 specific: + + - :c:func:`ThreadScan()` calls :c:func:`StackScan()` if the thread is current. + :c:func:`GetThreadContext()` doesn't work on the current thread (the + context would not necessarily have the values which were in the + saved registers on entry to the MM). + - Otherwise it calls :c:func:`GetThreadContext()` to get the root + registers and the stack pointer. + - The thread's registers are dumped into a :c:macro:`CONTEXT` structure and + fixed in memory. + - Scan the stack (getting the stack pointer from ``CONTEXT.Rsp``). + + +Issues +------ + +1. Scanning after exceptions. :c:func:`StackScan()` relies on the + non-preserved registers having been pushed on the stack. If we want + to scan after a fault we must make sure that these registers are + either already stored on the stack, or, have an extra function to + do this explicitly. + +2. Multiple registration. It is not clear whether a thread should be + allowed to be registered multiple times. We do not provide a + mechanism for knowing whether a thread is already registered with + an arena. + + diff --git a/mps/manual/html/_sources/design/thread-safety.txt b/mps/manual/html/_sources/design/thread-safety.txt index 05966e4b8c4..eb43b437ff3 100644 --- a/mps/manual/html/_sources/design/thread-safety.txt +++ b/mps/manual/html/_sources/design/thread-safety.txt @@ -1,6 +1,326 @@ +.. _design-thread-safety: + + .. index:: pair: thread safety; design -.. _design-thread-safety: -.. include:: ../../converted/thread-safety.rst +Thread safety in the MPS +======================== + +.. mps:prefix:: design.mps.thread-safety + + +Introduction +------------ + +:mps:tag:`intro` This describes how thread safety is achieved in the MPS. + + +Overview +-------- + +:mps:tag:`over` The MPS is expected to run in an environment with multiple +threads calling into the MPS. The initial approach is very simple. +Some of the code is known to operate with exclusive access to the data +it manipulates, so this code is safe. For the rest of the code, shared +data structures are locked by the use of a single binary lock +(design.mps.lock(0)) per arena. This lock is claimed on entry to the +MPS and released on exit from it. So there is at most a single thread +(per arena) running "inside" the MPS at a time. + + +Requirements +------------ + +:mps:tag:`req.mt` Code must work correctly in presence of multiple threads +all calling into the MPS. + +:mps:tag:`req.perf` Performance should not be unreasonably hindered. + + +Architecture +------------ + +:mps:tag:`arch.arena` Arena Lock: no shared data between arenas. + +:mps:tag:`arch.global.binary` Global binary lock: protects mutable data +shared between arenas -- that is, the arena ring, see +design.mps.arena.static.ring.lock. + +:mps:tag:`arch.global.recursive` Global recursive lock: protects static data +which must be initialized once -- for example, pool classes, see +design.mps.protocol.impl.init-lock. + +:mps:tag:`arch.other` Other: data not shared. + +:mps:tag:`arch.static` Static data: sigs: shared-non-mutable always inited +to same thing. + +:mps:tag:`arena-entry` Each arena has a single lock. Externally visible +calls fall into two categories. Simple: arena lock not held. Lock is +claimed on entry, and released on exit. Recall: These are callable +only after a call-back from the MPS. In this case a arena lock is +already held. + +:mps:tag:`interface` The definition of the interface should guarantee safe +use of calls (from a locking point of view). For example, a buffer +must be exclusive to a thread. + +:mps:tag:`buffers` The buffer code is designed not to need a lock in the +fast case. A lock is only claimed on the exceptional reserve, trip and +commit cases (fill and trip?). A buffer contains references to shared +data (via pool field). Accessing this shared data must involve a lock. + +:mps:tag:`deadlock` A strict ordering is required between the global and +arena locks to prevent deadlock. The binary global lock may not be +claimed while either the arena or recursive global lock is held; the +arena lock may not be claimed while the recursive global lock is held. +Each arena lock is independent of all other arena locks; that is, a +thread may not attempt to claim more than one arena lock at a time. + + +Analysis +-------- + +:mps:tag:`anal.simple` To have the code functioning correctly it should be +easy to change correctly. So a simple approach is desirable. We have +to also ensure that performance is not unreasonably downgraded. + +Performance cost of locking +........................... + +:mps:tag:`lock-cost` The cost of locking in performance terms are: + +- :mps:tag:`lock-cost.overhead` the overhead of claiming and releasing locks; + +- :mps:tag:`lock-cost.pause` the pauses caused by one thread being blocked + on another thread. + +- :mps:tag:`lock-cost.wait` the time wasted by one thread being blocked on + another thread. + +:mps:tag:`anal.perf.signif` :mps:ref:`.lock-cost.pause` is significant if there are +MPS functions that take a long time. Using more locks, e.g. having a +lock per pool as well as a lock per arena, is a way of decreasing the +locking conflict between threads (.lock-cost.pause and +:mps:ref:`.lock-cost.wait`). However this could increase +:mps:ref:`.lock-cost.overhead` significantly. + +:mps:tag:`anal.perf.work` But all MPS functions imply a small work-load +unless a collection is taking place. In the case of a collection, in +practice and certainly in the near future, all threads will most +likely be suspended while the collection work is going on. (The pages +being scanned will need to be unprotected which implies the mutator +will have to be stopped.) We also have to remember that unless we are +running on genuine multiprocessor :mps:ref:`.lock-cost.wait` is irrelevant. + +:mps:tag:`anal.perf.alloc` During typical use we expect that it is +allocation that is the most frequent activity. Allocation buffers +(design.mps.buffer) are designed to allow allocation in concurrent +threads without needing a lock. So the most significant time a thread +spends in the MPS will be on a buffer-fill or during a collection. The +next most significant use is likely to be buffer create and deletion, +as a separate buffer will be required for each thread. + +:mps:tag:`anal.perf.lock` So overall the performance cost of locking is, I +estimate, most significantly the overhead of calling the locking +functions. Hence it would be undesirable from a performance point of +view to have more than one lock. + +Recursive vs binary locks +......................... + +:mps:tag:`anal.reentrance` The simplest way to lock the code safely is to +define which code runs inside or outside the lock. Calling from the +outside to the inside implies a lock has to be claimed. Returning +means the lock has to be released. Control flow from outside to +outside and from inside to inside needs no locking action. To +implement this a function defined on the external interface needs to +claim the lock on entry and release it on exit. Our code currently +uses some external functions with the lock already held. There are two +ways to implement this: + +1. :mps:tag:`recursive` Each external function claims a recursive lock. + + - simple; + - have to worry about locking depth; + - extra locking overhead on internal calls of external functions; + +2. :mps:tag:`binary` Each external function claims a binary lock. Replace + each internal call of an external function with a call to a newly + defined internal one. + + - more code + - slightly easier to reason about + +:mps:tag:`anal.strategy` It seems that the :mps:ref:`.recursive` strategy is the +easiest to implement first, but could be evolved into a :mps:ref:`.binary` +strategy. (That evolution has now happened. tony 1999-08-31). + + +Ideas +----- + +:mps:tag:`sol.arena-lock` Lock per arena which locks all MPS structures +associated with the arena, except allocation buffers. + +:mps:tag:`sol.init` Shared static data may not be changed. It is initialised +before being read, and if re-initalised the values written must be +identical to those already there. Essentially only read-only shared +static data is allowed. + +:mps:tag:`sol.fine-grain` Use finer grained locks, for example, a lock per +per pool instance. Arena lock locks only operations on arena. Pool +locks are claimed per pool. An ordering on pool instances would avoid +deadlock. + +:mps:tag:`sol.global` Use global locks for genuinely global data which must +be updated dynamically. An ordering between global and arena locks +would avoid deadlock. + + +Implementation +-------------- + +Use MPS locks (design.mps.lock) to do locking. + +Locking Functions +................. + +:c:func:`ArenaEnter()` and :c:func:`ArenaLeave()` are used to claim and release the +arena lock. To implement this: + +- There is a lock for every arena. The arena class init function + allocates the lock as well as the arena itself. +- :c:func:`ArenaInit()` calls :c:func:`LockInit()` on the lock and initializes the + pointer to it from the arena. +- :c:func:`ArenaDestroy()` calls :c:func:`LockFinish()` on it. +- :c:func:`ArenaEnter()` claims the lock. +- :c:func:`ArenaLeave()` releases the lock. + +Shared and non-shared data +.......................... + +Non-shared data is data for which no other thread has a handle on it. +shared-non-mutable data is data which is never changed after +initialisation. It may be re-initialised, if re-initialisation does +not change its value. atomically updatable data is data which is not +locked, but may be shared because it is in a consistent state before +and after an update. + +A function is "safe" if it may safely execute without exclusive access +to the data it manipulates. + +A "safe" function may: + +- call other safe functions; +- manipulate non-shared data; +- read shared-non-mutable data; +- claim the arena lock around code which may manipulate shared data in + the arena. + +Each function in the external MPS interface falls into one of the +following categories: + +- calls :c:func:`ArenaEnter()` on entry and :c:func:`ArenaLeave()` on exit; +- uses :c:func:`PoolArena()` to identify the arena, before claiming the lock; +- uses :c:func:`BufferPool()` and :c:func:`PoolArena()` to identify the arena, before + claiming the lock; +- is not defined as external but is listed for explicitness; +- only claims the lock in otherwise unsafe situations (buffer code?); +- may be called externally but only in a situation where the arena + lock is already held; +- is the unique accessor of its data. + +So :c:func:`PoolArena()` and :c:func:`BufferPool()` must be "safe". ``pool->arena`` is +shared-non-mutable. ``buffer->pool`` is shared-non-mutable. + +Validation +.......... + +We have to be careful about validation. Any function that is called +from a arena-safe function without the arena-lock held, must itself be +safe, or manipulating non-shared data. + +For example, calling :c:func:`PoolIsValid()` before claiming the lock would be +wrong if :c:func:`PoolIsValid()` is unsafe. Defining it to be safe would +involve locking it, which if done in all similar situations would be +very expensive. + +Possibly remove validation from accessor methods; replace with +signature check and :c:func:`IsValid()` calls in callers of accessor +functions. + +Annotations?: +- safe +- non-shared +- shared-non-mutable + +Safe functions +.............. + +Arena + +- :c:func:`ArenaCreate()` -- no shared data; no lock; calls :c:func:`LockInit()`. +- :c:func:`ArenaDestroy()` -- no shared data; no lock (should only finish + arena after use); calls :c:func:`LockFinish()`. +- :c:func:`ArenaDescribe()` -- lock. + +Root (for the purposes of locking this module can be thought of as external) + +- :c:func:`RootCreate()` -- calls create +- :c:func:`RootCreateTable()` -- calls create +- create -- lock +- :c:func:`RootDestroy()` -- lock +- :c:func:`RootDescribe()` -- lock + +will be attached to arena, can lock now. + + +Pool + +- :c:func:`PoolCreate()` / :c:func:`PoolCreateV()` -- lock (Create calls CreateV which locks). +- :c:func:`PoolDestroy()` -- lock +- :c:func:`PoolAlloc()` -- lock +- :c:func:`PoolFree()` -- lock +- :c:func:`PoolArena()` -- accesses shared-non-mutable data only +- :c:func:`PoolDescribe()` -- lock + +Format + +- :c:func:`FormatCreate()` -- lock +- :c:func:`FormatDestroy()` -- lock + +Buffer + +- :c:func:`BufferCreate()` -- lock +- :c:func:`BufferDestroy()` -- lock +- :c:func:`BufferFill()` -- lock +- :c:func:`BufferTrip()` -- lock +- :c:func:`BufferPool()` -- accesses shared-non-mutable data only +- :c:func:`BufferDescribe()` -- lock +- :c:func:`BufferCommit()` -- "unsafe": buffer may be used by single thread + only. (but safe wrt arena) +- :c:func:`BufferReserve()` -- "unsafe": also + +PoolClass (only shared data is static and non-mutable) + +- :c:func:`PoolClass()` +- :c:func:`PoolClassAMC()` +- :c:func:`PoolClassMV()` +- :c:func:`PoolClassMFS()` + +Sig (as with :c:type:`PoolClass`, relies on static data reinitialised to +constant value) + +Collect + +- :c:func:`Collect()` -- lock + +Thread + +- :c:func:`ThreadRegister()` -- lock +- :c:func:`ThreadDeregister()` -- lock + + diff --git a/mps/manual/html/_sources/design/trace.txt b/mps/manual/html/_sources/design/trace.txt index caacef96048..8e75ee7807f 100644 --- a/mps/manual/html/_sources/design/trace.txt +++ b/mps/manual/html/_sources/design/trace.txt @@ -1,6 +1,237 @@ +.. _design-trace: + + .. index:: pair: tracer; design -.. _design-trace: -.. include:: ../../converted/trace.rst +Tracer +====== + +.. mps:prefix:: design.mps.trace + + +Introduction +------------ + +.. warning:: + + This document is currently a mixture of very old design notes (the + preformatted section immediately following) and some newer stuff. + It doesn't yet form anything like a complete picture. + + +Architecture +------------ + +:mps:tag:`instance.limit` There will be a limit on the number of traces that +can be created at any one time. This effectively limits the number of +concurrent traces. This limitation is expressed in the symbol +:c:macro:`TRACE_MAX`. + +.. note:: + + :c:macro:`TRACE_MAX` is currently set to 1, see request.mps.160020 + "Multiple traces would not work". David Jones, 1998-06-15. + +:mps:tag:`rate` See `mail.nickb.1997-07-31.14-37 `_. + +.. note:: + + Now revised? See request.epcore.160062 and + change.epcore.minnow.160062. David Jones, 1998-06-15. + +:mps:tag:`exact.legal` Exact references should either point outside the +arena (to non-managed address space) or to a tract allocated to a +pool. Exact references that are to addresses which the arena has +reserved but hasn't allocated memory to are illegal (the exact +reference couldn't possibly refer to a real object). Depending on the +future semantics of :c:func:`PoolDestroy()` we might need to adjust our +strategy here. See mail.dsm.1996-02-14.18-18 for a strategy of coping +gracefully with :c:func:`PoolDestroy()`. We check that this is the case in the +fixer. It may be sensible to make this check CRITICAL in certain +configurations. + +:mps:tag:`fix.fixed.all` ``ss->fixedSummary`` is accumulated (in the fixer) +for all the pointers whether or not they are genuine references. We +could accumulate fewer pointers here; if a pointer fails the +:c:func:`TractOfAddr()` test then we know it isn't a reference, so we needn't +accumulate it into the fixed summary. The design allows this, but it +breaks a useful post-condition on scanning (if the accumulation of +``ss->fixedSummary`` was moved the accuracy of ``ss->fixedSummary`` +would vary according to the "width" of the white summary). See +mail.pekka.1998-02-04.16-48 for improvement suggestions. + + +Analysis +-------- + +:mps:tag:`fix.copy-fail` Fixing can always succeed, even if copying the +referenced object has failed (due to lack of memory, for example), by +backing off to treating a reference as ambiguous. Assuming that fixing +an ambiguous reference doesn't allocate memory (which is no longer +true for AMC for example). See request.dylan.170560 for a slightly +more sophisticated way to proceed when you can no longer allocate +memory for copying. + + +Ideas +----- + +:mps:tag:`flip.after` To avoid excessive barrier impact on the mutator +immediately after flip, we could scan during flip other objects which +are "near" the roots, or otherwise known to be likely to be accessed +in the near future. + + +Implementation +-------------- + +Speed +..... + +:mps:tag:`fix` The fix path is critical to garbage collection speed. +Abstractly fix is applied to all the references in the non-white heap +and all the references in the copied heap. Remembered sets cut down +the number of segments we have to scan. The zone test cuts down the +number of references we call fix on. The speed of the remainder of the +fix path is still critical to system performance. Various +modifications to and aspects of the system are concerned with +maintaining the speed along this path. + +:mps:tag:`fix.tractofaddr` :c:func:`TractOfAddr()` is called on every reference that +passes the zone test and is on the critical path, to determine whether +the segment is white. There is no need to examine the segment to +perform this test, since whiteness information is duplicated in +tracts, specifically to optimize this test. :c:func:`TractOfAddr()` itself is +a simple class dispatch function (which dispatches to the arena +class's :c:func:`TractOfAddr()` method). Inlining the dispatch and inlining +the functions called by :c:func:`VMTractOfAddr()` makes a small but noticable +difference to the speed of the dylan compiler. + +:mps:tag:`fix.noaver` :c:func:`AVER()` statements in the code add bulk to the code +(reducing I-cache efficacy) and add branches to the path (polluting +the branch pedictors) resulting in a slow down. Removing all the +:c:func:`AVER()` statements from the fix path improves the overall speed of +the Dylan compiler by as much as 9%. + +:mps:tag:`fix.nocopy` :c:func:`AMCFix()` used to copy objects by using the format's +copy method. This involved a function call (through an indirection) +and in ``dylan_copy`` a call to ``dylan_skip`` (to recompute the +length) and call to ``memcpy`` with general parameters. Replacing this +with a direct call to ``memcpy`` removes these overheads and the call +to ``memcpy`` now has aligned parameters. The call to ``memcpy`` is +inlined by the C compiler. This change results in a 4–5% speed-up in +the Dylan compiler. + +:mps:tag:`reclaim` Because the reclaim phase of the trace (implemented by +:c:func:`TraceReclaim()`) examines every segment it is fairly time intensive. +rit's profiles presented in request.dylan.170551 show a gap between +the two varieties variety.hi and variety.wi. + +:mps:tag:`reclaim.noaver` Converting :c:func:`AVER()` statements in the loops of +:c:func:`TraceReclaim()`, :c:func:`PoolReclaim()`, :c:func:`AMCReclaim()` (:c:func:`LOReclaim()`? +:c:func:`AWLReclaim()`?) will result in a noticeable speed improvement. + +.. note:: + + Insert actual speed improvement here, if any. + + +Life cycle of a trace object +---------------------------- + +:c:func:`TraceCreate()` creates a trace in state ``TraceINIT`` + +Some segments get condemned (made white). + +:c:func:`TraceStart()` gets called which: + +- Derives an initial reference partition based on the existing + white set. The white zone set and the segments' summaries are used to + create an initial grey set. + +- Emits a :c:func:`GCStart()` message. + +- Initialises ``trace->rate`` by estimating the required scanning + rate. + +- Moves the trace into the state ``TraceUNFLIPPED``. + +- Immediately calls ``traceFlip`` which flips the trace and moves + it into state ``TraceFLIPPED``. + +Whilst a trace is alive every so often its ``traceQuantum`` method +gets invoked (via :c:func:`TracePoll()`) in order to do a quantum of tracing +work. ``traceQuantum`` is responsible for ticking through the trace's +top-level state machine. Most of the interesting work, the tracing, +happens in the ``TraceFLIPPED`` state. + +The trace transitions through its states in the following sequence: +``TraceINIT`` → (``TraceUNFLIPPED``) → ``TraceFLIPPED`` → +``TraceRECLAIM`` → ``TraceFINISHED``. + +Whilst ``TraceUNFLIPPED`` appears in the code, no trace does any work +in this state; all traces are immediately flipped to be in the +``TraceFLIPPED`` state (see above). + +Once the trace is in the ``TraceFINISHED`` state it performs no more +work and it can be safely destroyed. Generally the callers of +``traceQuantum`` will destroy the trace. + + +Making progress: scanning grey segments +....................................... + +Most of the interesting work of a trace, the actual tracing, happens +in the ``TraceFLIPPED`` state (work *would* happen in the +``TraceUNFLIPPED`` state, but that is not implemented). + +The tracer makes progress by choosing a grey segment to scan, and +scanning it. The actual scanning is performed by pools. + +Note that at all times a reference partition is maintained. + +The order in which the trace scans things determines the semantics of +certain types of references (in particular, weak and final +references). Or, to put it another way the desired semantics of weak +and final references impose certain restrictions on the order in which +the trace can scan things. + +The tracer uses a system of *reference ranks* (or just ranks) so that +it can impose an order on its scanning work. The ranks are ordered. + +The tracer proceeds band by band. The first band is all objects it can +reach by following references of the first rank. The second band is +all subsequent objects it can reach by following references of the +second and first ranks. The third band is all subsequent objects it +can reach by following references of the third, second, and first +ranks. And so on. The description of the tracer working like this +originated in [RHSK_2007-06-25]_. + +A trace keep track of which band it is tracing. This is returned by +the :c:func:`TraceBand()` method. Keeping this band information helps it +implement the semantics of finalization and weakness. The band used to +not be explicitly stored, but this hindered the implementation of good +finalization semantics (essentially in some circumstances finalization +messages were delayed by at least one collection cycle, see +job001658_. + +.. _job001658: https://info.ravenbrook.com/project/mps/issue/job001658/ + +The band is used when selecting a grey segment to scan (the selection +occurs in :c:func:`traceFindGrey()`). The tracer attempts to first find +segments whose rank is the current band, then segments whose rank is +previous to the current band, and so on. If there are no segments +found then the current band is exhausted and the current band is +incremented to the next rank. When the current band is moved through +all the ranks in this fashion there is no more tracing to be done. + + + +References +---------- + +.. [RHSK_2007-06-25] Richard Kistruck. Ravenbrook Limited. 2007-06-25. "`The semantics of rank-based tracing `__". + + diff --git a/mps/manual/html/_sources/design/type.txt b/mps/manual/html/_sources/design/type.txt index 6026dc77cbd..c931c65fafb 100644 --- a/mps/manual/html/_sources/design/type.txt +++ b/mps/manual/html/_sources/design/type.txt @@ -1,6 +1,522 @@ +.. _design-type: + + .. index:: pair: general types; design -.. _design-type: -.. include:: ../../converted/type.rst +General MPS types +================= + +.. mps:prefix:: design.mps.type + + +Introduction +------------ + +:mps:tag:`intro` See impl.h.mpmtypes. + + +Rationale +--------- + +Some types are declared to resolve a point of design, such as the best +type to use for array indexing. + +Some types are declared so that the intention of code is clearer. For +example, :c:type:`Byte` is necessarily ``unsigned char``, but it's better to +say :c:type:`Byte` in your code if it's what you mean. + + +Concrete types +-------------- + +.. c:type:: int Bool + +:mps:tag:`bool` The :c:type:`Bool` type is mostly defined so that the intention of +code is clearer. In C, Boolean expressions evaluate to ``int``, so +:c:type:`Bool` is in fact an alias for ``int``. + +:mps:tag:`bool.value` :c:type:`Bool` has two values, :c:macro:`TRUE` and :c:macro:`FALSE`. These +are defined to be ``1`` and ``0`` respectively, for compatibility with +C Boolean expressions (so one may set a :c:type:`Bool` to the result of a C +Boolean expression). + +:mps:tag:`bool.use` :c:type:`Bool` is a type which should be used when a Boolean +value is intended, for example, as the result of a function. Using a +Boolean type in C is a tricky thing. Non-zero values are "true" (when +used as control conditions) but are not all equal to :c:macro:`TRUE`. Use +with care. + +:mps:tag:`bool.check` :c:func:`BoolCheck()` simply checks whether the argument is +:c:macro:`TRUE` (``1``) or :c:macro:`FALSE` (``0``). + +:mps:tag:`bool.check.inline` The inline macro version of ``BoolCheck`` casts +the ``int`` to ``unsigned`` and checks that it is ``<= 1``. This is +safe, well-defined, uses the argument exactly once, and generates +reasonable code. + +:mps:tag:`bool.check.inline.smaller` In fact we can expect that the "inline" +version of :c:func:`BoolCheck()` to be smaller than the equivalent function +call. On IA-32 for example, a function call will be 3 instructions +(total 9 bytes), the inline code for :c:func:`BoolCheck()` will be 1 +instruction (total 3 bytes) (both sequences not including the test +which is the same length in either case). + +:mps:tag:`bool.check.inline.why` As well as being smaller (see +:mps:ref:`.bool.check.inline.smaller`) it is faster. On 1998-11-16 drj +compared ``w3i3mv\hi\amcss.exe`` running with and without the macro +for ``BoolCheck`` on the PC Aaron. "With" ran in 97.7% of the time +(averaged over 3 runs). + + +.. c:type:: int Res + +:mps:tag:`res` :c:type:`Res` is the type of result codes. A result code indicates +the success or failure of an operation, along with the reason for +failure. Like Unix error codes, the meaning of the code depends on the +call that returned it. These codes are just broad categories with +mnemonic names for various sorts of problems. + +=================== ======================================================= +Result code Description +=================== ======================================================= +``ResOK`` The operation succeeded. Return parameters may only be + updated if OK is returned, otherwise they must be left + untouched. +------------------- ------------------------------------------------------- +``ResFAIL`` Something went wrong which doesn't fall into any of the + other categories. The exact meaning depends on the + call. See documentation. +------------------- ------------------------------------------------------- +``ResRESOURCE`` A needed resource could not be obtained. Which resource + depends on the call. See also ``ResMEMORY``, which is a + special case of this. +------------------- ------------------------------------------------------- +``ResMEMORY`` Needed memory (committed memory, not address space) + could not be obtained. +------------------- ------------------------------------------------------- +``ResLIMIT`` An internal limitation was reached. For example, the + maximum number of somethings was reached. We should + avoid returning this by not including static + limitations in our code, as far as possible. (See + rule.impl.constrain and + rule.impl.limits.) +------------------- ------------------------------------------------------- +``ResUNIMPL`` The operation, or some vital part of it, is + unimplemented. This might be returned by functions + which are no longer supported, or by operations which + are included for future expansion, but not yet + supported. +------------------- ------------------------------------------------------- +``ResIO`` An I/O error occurred. Exactly what depends on the + function. +------------------- ------------------------------------------------------- +``ResCOMMIT_LIMIT`` The arena's commit limit would have been exceeded + as a result of allocation. +------------------- ------------------------------------------------------- +``ResPARAM`` An invalid parameter was passed. Normally reserved for + parameters passed from the client. +=================== ======================================================= + +:mps:tag:`res.use` :c:type:`Res` should be returned from any function which might +fail. Any other results of the function should be passed back in +"return" parameters (pointers to locations to fill in with the +results). + +.. note:: This is documented elsewhere, I think -- richard + +:mps:tag:`res.use.spec` The most specific code should be returned. + + +.. c:type:: void (*Fun)(void) + +:mps:tag:`fun` :c:type:`Fun` is the type of a pointer to a function about which +nothing more is known. + +:mps:tag:`fun.use` :c:type:`Fun` should be used where it's necessary to handle a +function in a polymorphic way without calling it. For example, if you +need to write a function ``g`` which passes another function ``f`` +through to a third function ``h``, where ``h`` knows the real type of +``f`` but ``g`` doesn't. + + +.. c:type:: MPS_T_WORD Word + +:mps:tag:`word` :c:type:`Word` is an unsigned integral type which matches the size +of the machine word, that is, the natural size of the machine +registers and addresses. + +:mps:tag:`word.use` :c:type:`Word` should be used where an unsigned integer is +required that might range as large as the machine word. + +:mps:tag:`word.source` :c:type:`Word` is derived from the macro :c:macro:`MPS_T_WORD` +which is declared in impl.h.mpstd according to the target platform +(design.mps.config.pf.word). + +:mps:tag:`word.conv.c` :c:type:`Word` is converted to :c:type:`mps_word_t` in the MPS C +Interface. + +:mps:tag:`word.ops` :c:func:`WordIsAligned()`, :c:func:`WordAlignUp()`, +:c:func:`WordAlignDown()` and :c:func:`WordRoundUp()`. + + +.. c:type:: unsigned char Byte + +:mps:tag:`byte` :c:type:`Byte` is an unsigned integral type corresponding to the +unit in which most sizes are measured, and also the units of +``sizeof``. + +:mps:tag:`byte.use` :c:type:`Byte` should be used in preference to ``char`` or +``unsigned char`` wherever it is necessary to deal with bytes +directly. + +:mps:tag:`byte.source` :c:type:`Byte` is a just pedagogic version of ``unsigned +char``, since ``char`` is the unit of ``sizeof``. + + +.. c:type:: Word Index + +:mps:tag:`index` :c:type:`Index` is an unsigned integral type which is large +enough to hold any array index. + +:mps:tag:`index.use` :c:type:`Index` should be used where the maximum size of the +array cannot be statically determined. If the maximum size can be +determined then the smallest unsigned integer with a large enough +range may be used instead. + + +.. c:type:: Word Count + +:mps:tag:`count` :c:type:`Count` is an unsigned integral type which is large +enough to hold the size of any collection of objects in the MPS. + +:mps:tag:`count.use` :c:type:`Count` should be used for a number of objects +(control or managed) where the maximum number of objects cannot be +statically determined. If the maximum number can be statically +determined then the smallest unsigned integer with a large enough +range may be used instead (although :c:type:`Count` may be preferable for +clarity). + +.. note:: + + Should :c:type:`Count` be used to count things that aren't represented + by objects (for example, a level)? I would say yes. gavinm + 1998-07-21 + +.. note:: + + Only where it can be determined that the maximum count is less + than the number of objects. pekka 1998-07-21 + + +.. c:type:: Word Accumulation + +:mps:tag:`accumulation` :c:type:`Accumulation` is an arithmetic type which is +large enough to hold accumulated totals of objects of bytes (for +example, total number of objects allocated, total number of bytes +allocated). + +:mps:tag:`accumulation.type` Currently it is ``double``, but the reason for +the interface is so that we can more easily change it if we want to +(if we decide we need more accuracy for example). + +:mps:tag:`accumulation.use` Currently the only way to use an +:c:type:`Accumulation` is to reset it (by calling ``AccumulatorReset``) and +accumulate amounts into it (by calling ``Accumulate``). There is no +way to read it at the moment, but that's okay, because no one seems to +want to. + +:mps:tag:`accumulation.future` Probably we should have methods which return +the accumulation into an ``unsigned long``, and also a ``double``; +these functions should return :c:type:`Bool` to indicate whether the +accumulation can fit in the requested type. Possibly we could have +functions which returned scaled accumulations. For example, +``AccumulatorScale(a, d)`` would divide the ``Accumulation a`` by +``double d`` and return the ``double`` result if it fitted into a +``double``. + + +.. c:type:: struct AddrStruct *Addr + +:mps:tag:`addr` :c:type:`Addr` is the type used for "managed addresses", that is, +addresses of objects managed by the MPS. + +:mps:tag:`addr.def` :c:type:`Addr` is defined as ``struct AddrStruct *``, but +:c:type:`AddrStruct` is never defined. This means that :c:type:`Addr` is always an +incomplete type, which prevents accidental dereferencing, arithmetic, +or assignment to other pointer types. + +:mps:tag:`addr.use` :c:type:`Addr` should be used whenever the code needs to deal +with addresses. It should not be used for the addresses of memory +manager data structures themselves, so that the memory manager remains +amenable to working in a separate address space. Be careful not to +confuse :c:type:`Addr` with ``void *``. + +:mps:tag:`addr.ops` Limited arithmetic is allowed on addresses using +:c:func:`AddrAdd()` and :c:func:`AddrOffset()` (impl.c.mpm). Addresses may also be +compared using the relational operators ``==``, ``!=``, ``<``, ``<=``, +``>``, and ``>=``. + +:mps:tag:`addr.ops.mem` We need efficient operators similar to :c:func:`memset()`, +:c:func:`memcpy()`, and :c:func:`memcmp()` on :c:type:`Addr`; these are called :c:func:`AddrSet()`, +:c:func:`AddrCopy()`, and :c:func:`AddrComp()`. When :c:type:`Addr` is compatible with +``void *``, these are implemented through the functions +:c:func:`mps_lib_memset()`, :c:func:`mps_lib_memcpy()`, and :c:func:`mps_lib_memcmp()` +functions in the plinth (impl.h.mpm). + +.. note:: + + No other implementation exists at present. pekka 1998-09-07 + +:mps:tag:`addr.conv.c` :c:type:`Addr` is converted to :c:type:`mps_addr_t` in the MPS C +Interface. :c:type:`mps_addr_t` is defined to be the same as ``void *``, so +using the MPS C Interface confines the memory manager to the same +address space as the client data. + + +.. c:type:: Word Size + +:mps:tag:`size` :c:type:`Size` is an unsigned integral type large enough to +hold the size of any object which the MPS might manage. + +:mps:tag:`size.byte` :c:type:`Size` should hold a size calculated in bytes. + +.. warning:: This may not be true for all existing code. + +:mps:tag:`size.use` :c:type:`Size` should be used whenever the code needs to deal +with the size of managed memory or client objects. It should not be +used for the sizes of the memory manager's own data structures, so +that the memory manager is amenable to working in a separate address +space. Be careful not to confuse it with ``size_t``. + +:mps:tag:`size.ops` :c:func:`SizeIsAligned()`, :c:func:`SizeAlignUp()`, +:c:func:`SizeAlignDown()` and :c:func:`SizeRoundUp()`. + +:mps:tag:`size.conv.c` :c:type:`Size` is converted to ``size_t`` in the MPS C +Interface. This constrains the memory manager to the same address +space as the client data. + + +.. c:type:: Word Align + +:mps:tag:`align` :c:type:`Align` is an unsigned integral type which is used to +represent the alignment of managed addresses. All alignments are +positive powers of two. :c:type:`Align` is large enough to hold the maximum +possible alignment. + +:mps:tag:`align.use` :c:type:`Align` should be used whenever the code needs to +deal with the alignment of a managed address. + +:mps:tag:`align.conv.c` :c:type:`Align` is converted to :c:type:`mps_align_t` in the MPS +C Interface. + + +.. c:type:: unsigned Shift + +:mps:tag:`shift` :c:type:`Shift` is an unsigned integral type which can hold the +amount by which a :c:type:`Word` can be shifted. It is therefore large +enough to hold the word width (in bits). + +:mps:tag:`shift.use` :c:type:`Shift` should be used whenever a shift value (the +right-hand operand of the ``<<`` or ``>>`` operators) is intended, to +make the code clear. It should also be used for structure fields which +have this use. + +:mps:tag:`shift.conv.c` :c:type:`Shift` is converted to :c:type:`mps_shift_t` in the MPS +C Interface. + + +.. c:type:: Addr Ref + +:mps:tag:`ref` :c:type:`Ref` is a reference to a managed object (as opposed to any +old managed address). :c:type:`Ref` should be used where a reference is +intended. + +.. note:: This isn't too clear -- richard + + +.. c:type:: Word RefSet + +:mps:tag:`refset` ``RefSet`` is a conservative approximation to a set of +references. See design.mps.refset. + + +.. c:type:: unsigned Rank + +:mps:tag:`rank` :c:type:`Rank` is an enumeration which represents the rank of a +reference. The ranks are: + +============= ===== ===================================================== +Rank Index Description +============= ===== ===================================================== +``RankAMBIG`` 0 The reference is ambiguous. That is, it must be + assumed to be a reference, but not updated in case it + isn't. +------------- ----- ----------------------------------------------------- +``RankEXACT`` 1 The reference is exact, and refers to an object. +------------- ----- ----------------------------------------------------- +``RankFINAL`` 2 The reference is exact and final, so special action + is required if only final or weak references remain + to the object. +------------- ----- ----------------------------------------------------- +``RankWEAK`` 3 The reference is exact and weak, so should be deleted + if only weak references remain to the object. +============= ===== ===================================================== + +:c:type:`Rank` is stored with segments and roots, and passed around. + +:c:type:`Rank` is converted to :c:type:`mps_rank_t` in the MPS C Interface. + +The ordering of the ranks is important. It is the order in which the +references must be scanned in order to respect the properties of +references of the ranks. Therefore they are declared explicitly with +their integer values. + +.. note:: Could :c:type:`Rank` be a ``short``? + +.. note:: + + This documentation should be expanded and moved to its own + document, then referenced from the implementation more thoroughly. + + +.. c:type:: Size Epoch + +:mps:tag:`epoch` An :c:type:`Epoch` is a count of the number of flips that have +occurred. It is used in the implementation of location dependencies. + +:c:type:`Epoch` is converted to :c:type:`mps_word_t` in the MPS C Interface, as a +field of :c:type:`mps_ld_s`. + + +.. c:type:: unsigned TraceId + +:mps:tag:`traceid` A :c:type:`TraceId` is an unsigned integer which is less than +:c:macro:`TRACE_MAX`. Each running trace has a different :c:type:`TraceId` which is +used to index into tables and bitfields used to remember the state of +that trace. + + +.. c:type:: unsigned TraceSet + +:mps:tag:`traceset` A :c:type:`TraceSet` is a bitset of :c:type:`TraceId`, +represented in the obvious way:: + + member(ti, ts) ⇔ ((1<base`` is the aligned pointer, +``vm->block`` is the pointer returned by :c:func:`malloc()`, which is used +in :c:func:`VMDestroy()`. + + diff --git a/mps/manual/html/_sources/design/vmo1.txt b/mps/manual/html/_sources/design/vmo1.txt new file mode 100644 index 00000000000..6715c5bd01f --- /dev/null +++ b/mps/manual/html/_sources/design/vmo1.txt @@ -0,0 +1,55 @@ +.. _design-vmo1: + + +.. index:: + pair: VM for Digital Unix; design + + +VM for Digital Unix +=================== + +.. mps:prefix:: design.mps.vmo1 + + +.. warning:: + + As of 2013-05-26, the MPS is no longer supported on Digital Unix, + so this document is only of historical interest. + + +Introduction +------------ + +:mps:tag:`readership` Any MPS developer. + +:mps:tag:`intro` This is the design of the VM Module for Digital UNIX (also +known as OSF/1 and Tru64 Unix; see os.o1). In general aspects +(including interface) the design is as for design.mps.vm. + + +Functions +--------- + +:mps:tag:`fun.unmap` :c:func:`VMUnmap()` "unmaps" a region by replacing the +existing mapping with a mapping using the ``vm->none_fd`` file +descriptor (see mumble mumble, :c:func:`VMCreate()`), and protection set to +:c:macro:`PROT_NONE` (that is, no access). + +:mps:tag:`fun.unmap.justify` Replacing the mapping in this way means that +the address space is still reserved and will not be used by calls to +:c:func:`mmap()` (perhaps in other libraries) which specify +:c:macro:`MAP_VARIABLE`. + +:mps:tag:`fun.unmap.offset` The offset for this mapping is the offset of the +region being unmapped in the VM; this gives the same effect as if +there was one mapping of the ``vm->none_fd`` from the base to the +limit of the VM (but "behind" all the other mappings that have been +created). + +:mps:tag:`fun.unmap.offset.justify` If this is not done (if for example the +offset is always specified as 0) then the VM will cause the kernel to +create a new file reference for each mapping created with +:c:func:`VMUnmap()`; eventually the kernel refuses the :c:func:`mmap()` call because it +can't create a new file reference. + + diff --git a/mps/manual/html/_sources/design/vmso.txt b/mps/manual/html/_sources/design/vmso.txt new file mode 100644 index 00000000000..561a07dec5e --- /dev/null +++ b/mps/manual/html/_sources/design/vmso.txt @@ -0,0 +1,140 @@ +.. _design-vmso: + + +.. index:: + pair: VM for Solaris; design + + +VM for Solaris +============== + +.. mps:prefix:: design.mps.vmso + +.. warning:: + + As of 2013-05-26, the MPS is no longer supported on Solaris, so + this document is only of historical interest. + + +Introduction +------------ + +:mps:tag:`intro` This is the design for the VM implementation on Solaris 2.x +(see os.so for OS details). The implementation is in MMsrc!vmso.c +(impl.c.vm). The design follows the design for and implements the +contract of the generic VM interface (design.mps.vm). To summarize: +The VM module provides a mechanism to reserve large (relative to the +amount of RAM) amounts of address space, and functions to map (back +with RAM) and unmap portions of this address space. + +:mps:tag:`source` Much of the implementation (and hence the design) was +inherited from the SunOS4 implementation. Not that there's any design +for that. You'll find the ``mmap(2)`` (for the system call :c:func:`mmap()`) +and the ``zero(7d)`` (for the device ``/dev/zero``) man pages useful +as well. The generic interface and some generic design is in +design.mps.vm. + + +Definitions +----------- + +:mps:tag:`def` See design.mps.vm.def.* for definitions common to all VMs. + + +Overview +-------- + +:mps:tag:`over` The system calls :c:func:`mmap()` and :c:func:`munmap()` are used to +access the underlying functionality. They are used in slightly unusual +ways, typically to overcome baroque features or implementation details +of the operating system. + +:mps:tag:`over.reserve` In order to reserve address space, a mapping to a +file (``/etc/passwd`` as it happens) is created with no protection +allowed. + +:mps:tag:`over.map` In order to map memory, a mapping to ``/dev/zero`` is +created. + +:mps:tag:`over.destroy` When the VM is destroyed, :c:func:`munmap()` is used to +remove all the mappings previously created. + + +Implementation +-------------- + +:mps:tag:`impl.create` :c:func:`VMCreate()` + +:mps:tag:`impl.create.vmstruct` Enough pages to hold the :c:type:`VMStruct` are +allocated by creating a mapping to ``/dev/zero`` (a read/write private +mapping), and using initializing the memory as a :c:type:`VMStruct`. + +:mps:tag:`impl.create.reserve` The size parameter is rounded up to page size +and this amount of address space is reserved. The address space is +reserved by creating a shared mapping to ``/etc/passwd`` with no +access allowed (the ``prot`` argument is :c:macro:`PROT_NONE`, and the +``flags`` argument is :c:macro:`MAP_SHARED`). + +:mps:tag:`impl.create.reserve.mmap.justify` :c:func:`mmap()` gives us a flexible +way to allocate address space without interfering with any other +component in the process. Because we don't specify :c:macro:`MAP_FIXED` we +are guaranteed to get a range of addresses that are not in use. Other +components must cooperate by not attempting to create mappings +specifying :c:macro:`MAP_FIXED` and an address in the range that the MPS has +reserved. + +:mps:tag:`impl.create.reserve.passwd.justify` Mapping ``/etc/passwd`` like +this worked on SunOS 4 (so this implementation inherited it). Mapping +``/dev/zero`` with ``prot=PROT_NONE`` and ``flags=MAP_PRIVATE`` does +not work because Solaris gratuitously allocates swap (even though you +can't use the memory). + +:mps:tag:`impl.create.reserve.improve` However, it would appears that or-ing +in :c:macro:`MAP_NORESERVE` mapping ``/dev/zero`` will reserve address space +without allocating swap, so this might be worth trying. That is, with +``prot=PROT_NONE`` and ``flags=MAP_PRIVATE|MAP_NORESERVE``. However +the following caveat comes from the original implementation: +"Experiments have shown that attempting to reserve address space by +mapping ``/dev/zero`` results in swap being reserved. This appears to +be a bug, so we work round it by using ``/etc/passwd``, the only file +we can think of which is pretty much guaranteed to be around." So that +might not work after all. + +:mps:tag:`impl.map` :c:func:`VMMap()` + +:mps:tag:`impl.map.zero` A mapping to ``/dev/zero`` is created at the +relevant addresses (overriding the map to ``/etc/passwd`` that was +previously in place for those addresses). The ``prot`` argument is +specified as ``PROT_READ|PROT_WRITE|PROT_EXEC`` (so that any access is +allowed), the ``flags`` argument as ``MAP_PRIVATE|MAP_FIXED``. The +flag :c:macro:`MAP_PRIVATE` means that the mapping is not shared with child +processes (child processes will have a mapping, but changes to the +memory will not be shared). The flag :c:macro:`MAP_FIXED` guarantees that we +get the mapping at the specified address). The ``zero(7d)`` man page +documents this as a way to create a "zero-initialized unnamed memory +object". + +:mps:tag:`impl.map.error` If there's not enough swap space for the mapping, +:c:func:`mmap()` will return :c:macro:`EAGAIN`, not :c:macro:`ENOMEM`, although you might +not think so from the man page. + +:mps:tag:`impl.unmap` :c:func:`VMUnmap()` + +:mps:tag:`impl.unmap.reserve` The relevant addresses are returned to the +reserved state by creating a mapping to ``/etc/passwd`` (overriding +the map ``/dev/zero`` that was previously in place for those +addresses). As for :c:func:`VMCreate()` (see :mps:ref:`.impl.create.reserve` above) +the ``prot`` argument is :c:macro:`PROT_NONE`, but the ``flags`` argument has +the addition :c:macro:`MAP_FIXED` flags (so is ``MAP_SHARED|MAP_FIXED``). + +:mps:tag:`impl.unmap.reserve.offset` The offset argument is specified to be +the offset of the addresses being unmapped from the base of the +reserved VM area. + +:mps:tag:`impl.unmap.reserve.offset.justify` Not specifying the offset like +this makes Solaris create a separate mapping (in the kernel) each time +Unmap is used, eventually the call to :c:func:`mmap()` will fail. Specifying +offset like this does not cause Solaris to create any extra mappings, +the existing mapping to ``/etc/passwd`` gets reused. + + diff --git a/mps/manual/html/_sources/design/writef.txt b/mps/manual/html/_sources/design/writef.txt index a771bd72375..6d442ee1f01 100644 --- a/mps/manual/html/_sources/design/writef.txt +++ b/mps/manual/html/_sources/design/writef.txt @@ -1,6 +1,107 @@ +.. _design-writef: + + .. index:: pair: WriteF function; design -.. _design-writef: -.. include:: ../../converted/writef.rst +The WriteF function +=================== + +.. mps:prefix:: design.mps.writef + + +Introduction +------------ + +:mps:tag:`intro` This document describes the :c:func:`WriteF()` function, which +allows formatted output in a manner similar to ANSI C ``printf``, but +allows the MPM to operate in a freestanding environment (see +design.mps.exec-env). + +:mps:tag:`background` The documents design.mps.exec-env and design.mps.lib +describe the design of the library interface and the reason that it +exists. + + +Design +------ + +:mps:tag:`no-printf` There is no dependency on :c:func:`printf()`. The MPM only +depends on :c:func:`fputc()` and :c:func:`fputs()`, via the Library Interface +(design.mps.lib). This makes it much easier to deploy the MPS in a +freestanding environment. This is achieved by implementing our own +internal output routines in mpm.c. + +Our output requirements are few, so the code is short. The only output +function which should be used in the rest of the MPM is :c:func:`WriteF()`, +which is similar to :c:func:`fprintf()`: + +.. c:function:: Res WriteF(mps_lib_FILE *stream, ...) + +:c:func:`WriteF()` expects a format string followed by zero or more items to +insert into the output, followed by another format string, more items, +and so on, and finally a :c:macro:`NULL` format string. For example:: + + WriteF(stream, + "Hello: $A\n", address, + "Spong: $U ($S)\n", number, string, + NULL); + +This makes :c:func:`Describe()` methods much easier to write. For example, :c:func:`BufferDescribe()` might contain the following code:: + + WriteF(stream, + "Buffer $P ($U) {\n", (WriteFP)buffer, (WriteFU)buffer->serial, + " base $A init $A alloc $A limit $A\n", + (WriteFA)buffer->base, (WriteFA)buffer->ap.init, + (WriteFA)buffer->ap.alloc, (WriteFA)buffer->ap.limit, + " Pool $P\n", (WriteFP)buffer->pool, + " Seg $P\n", (WriteFP)buffer->seg, + " rank $U\n", (WriteFU)buffer->rank, + " alignment $W\n", (WriteFW)buffer->alignment, + " grey $B\n", (WriteFB)buffer->grey, + " shieldMode $B\n", (WriteFB)buffer->shieldMode, + " p $P i $U\n", (WriteFP)buffer->p, (WriteFU)buffer->i, + "} Buffer $P ($U)\n", (WriteFP)buffer, (WriteFU)buffer->serial, + NULL); + +:mps:tag:`types` For each format ``$X`` that :c:func:`WriteF()` supports, there is a +type defined in impl.h.mpmtypes :c:func:`WriteFX()` which is the promoted +version of that type. These are provided both to ensure promotion and +to avoid any confusion about what type should be used in a cast. It is +easy to check the casts against the formats to ensure that they +correspond. + +:mps:tag:`types.future` It is possibly that this type set or similar may be +used in future in some generalisation of varargs in the MPS. + +:mps:tag:`formats` The formats supported are as follows. + +======= =========== ================== ====================================== +Code Bame Type Example rendering +======= =========== ================== ====================================== +``$A`` address :c:type:`Addr` ``000000019EF60010`` +``$P`` pointer ``void *`` ``000000019EF60100`` +``$F`` function ``void *(*)()`` ``0001D69E01000000`` (see `.f`_) +``$S`` string ``char *`` ``hello`` +``$C`` character ``char`` ``x`` +``$W`` word :c:type:`ULongest` ``0000000000109AE0`` +``$U`` decimal :c:type:`ULongest` ``42`` +``$B`` binary :c:type:`ULongest` ``00000000000000001011011110010001`` +``$$`` dollar -- ``$`` +======= =========== ================== ====================================== + +Note that ``WriteFC`` is an ``int``, because that is the default +promotion of a ``char`` (see :mps:ref:`.types`). + +:mps:tag:`snazzy` We should resist the temptation to make :c:func:`WriteF()` an +incredible snazzy output engine. We only need it for :c:func:`Describe()` +methods and assertion messages. At the moment it's a very simple bit +of code -- let's keep it that way. + +_`.f`: The ``F`` code is used for function pointers. ISO C forbids casting +function pointers to other types, so the bytes of their representation are +written sequentially, and may have a different endianness to other pointers. +Could be smarter, or even look up function names, but see :mps:ref:`.snazzy`. + + diff --git a/mps/manual/html/_sources/glossary/_Sidebar.txt b/mps/manual/html/_sources/glossary/_Sidebar.txt new file mode 100644 index 00000000000..e0e50826e07 --- /dev/null +++ b/mps/manual/html/_sources/glossary/_Sidebar.txt @@ -0,0 +1,2 @@ +* `Ravenbrook `_ +* `Google `_ diff --git a/mps/manual/html/_sources/glossary/home.txt b/mps/manual/html/_sources/glossary/home.txt new file mode 100644 index 00000000000..bb55975bf97 --- /dev/null +++ b/mps/manual/html/_sources/glossary/home.txt @@ -0,0 +1,33 @@ +.. _glossary: + +Memory Management Glossary +************************** + +.. include:: alphabet.txt + +.. toctree:: + :maxdepth: 1 + + a + b + c + d + e + f + g + h + i + k + l + m + n + o + p + q + r + s + t + u + v + w + z diff --git a/mps/manual/html/_sources/guide/advanced.txt b/mps/manual/html/_sources/guide/advanced.txt index a6604daf573..6835359e717 100644 --- a/mps/manual/html/_sources/guide/advanced.txt +++ b/mps/manual/html/_sources/guide/advanced.txt @@ -876,7 +876,11 @@ Segregation of objects When objects of different types have different properties (different sizes, lifetimes, references, layouts) it makes sense to segregate -them into pools of appropriate classes. +them into pools of appropriate classes. The garbage collector in the +MPS is designed to work efficiently with many pools: it traces +references between objects in different pools, and it coordinates the +scanning of the :term:`registers` and :term:`control stacks` (see +:ref:`topic-root-thread`). For example, the toy Scheme interpreter has a mixture of object types, some of which contain references to other objects (for example, pairs) diff --git a/mps/manual/html/_sources/guide/debug.txt b/mps/manual/html/_sources/guide/debug.txt index c38e09dbbec..e9fc93f951e 100644 --- a/mps/manual/html/_sources/guide/debug.txt +++ b/mps/manual/html/_sources/guide/debug.txt @@ -87,11 +87,11 @@ General debugging advice set dont-handle-bad-access 1 handle SIGBUS pass nostop noprint - If you are using GDB on Linux, run this command:: + If you are using GDB on Linux or FreeBSD, run this command:: - handle SIGXFSZ pass nostop noprint + handle SIGSEGV pass nostop noprint - (On either operating system, you can add these commands to your + (On these operating systems, you can add these commands to your ``.gdbinit`` if you always want them to be run.) diff --git a/mps/manual/html/_sources/guide/overview.txt b/mps/manual/html/_sources/guide/overview.txt index 322d9ea6258..02925361069 100644 --- a/mps/manual/html/_sources/guide/overview.txt +++ b/mps/manual/html/_sources/guide/overview.txt @@ -111,21 +111,24 @@ for example by calling :c:func:`mps_free`) and others are **scan** for :term:`references` to allocated blocks. See :ref:`topic-scanning`. -The arena needs you to tell it how to find your **roots**: references to -allocated blocks that are stored in static data, in memory not managed -by the MPS, or on your program's :term:`registers` or +The arena needs you to tell it how to find your **roots**: references +to allocated blocks that are stored in static data, in memory not +managed by the MPS, in your program's :term:`registers`, or on its :term:`control stack`. See :ref:`topic-root`. The MPS is designed to work with multi-threaded programs. Functions in -the C interface are thread safe, except in a few documented -cases. See :ref:`topic-thread`. The :term:`allocation point -protocol` provides fast lock-free allocation on multiple threads -simultaneously. See :ref:`topic-allocation`. +the C interface are thread safe, except in a few documented cases. See +:ref:`topic-thread`. The :term:`allocation point protocol` provides +fast lock-free allocation on multiple threads simultaneously. See +:ref:`topic-allocation`. The garbage collector is :term:`incremental `: it proceeds in small steps interleaved with the execution -of your program, so there are no long waits. See -:ref:`topic-collection`. +collection>`: it proceeds in small steps interleaved with the +execution of your program, so there are no long waits. The garbage +collector is designed to work efficiently with multiple pools, and +in cases where there are many references between objects in different +pools. See :ref:`topic-collection`. + What next? diff --git a/mps/manual/html/_sources/pool/intro.txt b/mps/manual/html/_sources/pool/intro.txt index 2346e1637bd..ca7d1265edc 100644 --- a/mps/manual/html/_sources/pool/intro.txt +++ b/mps/manual/html/_sources/pool/intro.txt @@ -92,7 +92,7 @@ Property AMC AMCZ AMS AWL LO ============================================= ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== Supports :c:func:`mps_alloc`? no no no no no yes yes yes no no Supports :c:func:`mps_free`? no no no no no yes yes yes yes no -Supports allocation points? yes yes yes yes yes no no yes yes yes +Supports allocation points? yes yes yes yes yes no yes yes yes yes Supports allocation frames? yes yes yes yes yes no no yes yes yes Supports segregated allocation caches? no no no no no yes yes yes no no Timing of collections? [2]_ auto auto auto auto auto --- --- --- --- --- diff --git a/mps/manual/html/_sources/pool/mfs.txt b/mps/manual/html/_sources/pool/mfs.txt index b13870006df..66071937bcf 100644 --- a/mps/manual/html/_sources/pool/mfs.txt +++ b/mps/manual/html/_sources/pool/mfs.txt @@ -11,13 +11,16 @@ MFS (Manual Fixed Small) :term:`pool class` for small objects of fixed size. Unlike other manual pool classes, it is not subject to :term:`internal -fragmentation`. +fragmentation`: if the population remains bounded, the memory usage +remains bounded too. On the other hand, unlike :ref:`pool-mvt` and +:ref:`pool-mvff` it does not return unused memory to the arena for +reuse by other pools. -The implementation is very simple: unlike other :term:`pool classes` -which store their control structures separately from the allocated -blocks, MFS maintains a stack of free blocks using a pointer in the -free block. :c:func:`mps_alloc` pops this stack and :c:func:`mps_free` -pushes it. +The implementation is very simple: unlike most other :term:`pool +classes` which store their control structures separately from the +allocated blocks, MFS maintains a stack of free blocks using a pointer +in the free block. :c:func:`mps_alloc` pops this stack and +:c:func:`mps_free` pushes it. .. index:: diff --git a/mps/manual/html/_sources/pool/mvff.txt b/mps/manual/html/_sources/pool/mvff.txt index 2fc57c59c66..c7f358abcdb 100644 --- a/mps/manual/html/_sources/pool/mvff.txt +++ b/mps/manual/html/_sources/pool/mvff.txt @@ -202,7 +202,7 @@ MVFF interface :c:macro:`MPS_KEY_MVFF_SLOT_HIGH`, and :c:macro:`MPS_KEY_MVFF_FIRST_FIT` are as described above, and :c:macro:`MPS_KEY_POOL_DEBUG_OPTIONS` specifies the debugging - :c:options. See :c:type:`mps_debug_option_s`. + options. See :c:type:`mps_debug_option_s`. .. deprecated:: starting with version 1.112. diff --git a/mps/manual/html/_sources/topic/collection.txt b/mps/manual/html/_sources/topic/collection.txt index bad646ea8e5..af1c18dfce9 100644 --- a/mps/manual/html/_sources/topic/collection.txt +++ b/mps/manual/html/_sources/topic/collection.txt @@ -13,6 +13,13 @@ Garbage collection ================== +The :term:`arena` contains a :term:`garbage collector` that +coordinates the collection of garbage in all of its +:term:`automatically managed ` +:term:`pools`. The collector efficiently traces references between +:term:`roots` and pools, and between objects in different pools. It is +capable of collecting many automatically managed pools simultaneously. + .. index:: single: chain; generation diff --git a/mps/manual/html/_sources/topic/error.txt b/mps/manual/html/_sources/topic/error.txt index 805f50af3bf..abf1a1c2600 100644 --- a/mps/manual/html/_sources/topic/error.txt +++ b/mps/manual/html/_sources/topic/error.txt @@ -188,13 +188,43 @@ than the client program, but it is not unknown, so if you have made every effort to track down the cause (see :ref:`guide-debug`) without luck, :ref:`get in touch `. + +.. index:: + single: assertion + single: error handling; assertion; assertion handling + +.. _topic-error-assertion-handling: + +Assertion handling +.................. + +When the MPS detects an assertion failure, it calls the :term:`plinth` +function :c:func:`mps_lib_assert_fail`. Unless you have replaced the plinth, this behaves as follows: + +- In the :term:`cool` :term:`variety`, print the assertion message to + standard error and terminate the program by calling :c:func:`abort`. + +- In the :term:`hot` and :term:`rash` varieties, print the assertion + message to standard error and do *not* terminate the program. + +You can change this behaviour by providing your own plinth, or using +:c:func:`mps_lib_assert_fail_install`. + +In many applications, users don't want their program terminated when +the MPS detects an error, no matter how severe. A lot of MPS +assertions indicate that the program is going to crash very soon, but +there still may be a chance for a user to get some useful results or +save their work. This is why the default assertion handler only +terminates in the :term:`cool` :term:`variety`. + + .. index:: single: assertion; common causes .. _topic-error-cause: Common assertions and their causes ----------------------------------- +.................................. This section lists some commonly encountered assertions and suggests likely causes. If you encounter an assertion not listed here (or an @@ -264,30 +294,6 @@ this documentation. condition? -.. index:: - single: assertion - single: error handling; assertion; assertion handling - -.. _topic-error-assertion-handling: - -Assertion Handling ------------------- - -When the MPS detects an assertion failure, it calls the :term:`plinth` -function :c:func:`mps_lib_assert_fail`. If you have not replaced the -plinth, this will print the assertion message and terminate the program -in the :term:`cool` :term:`variety`, but *not* in the :term:`hot` or -:term:`rash` varieties. You can change this behaviour by providing your -own plinth, or using :c:func:`mps_lib_assert_fail_install`. - -In many applications, users don't want their program terminated when the -MPS detects an error, no matter how severe. A lot of MPS assertions -indicate that the program is going to crash very soon, but there still -may be a chance for a user to get some useful results or save their -work. This is why the default assertion handler only terminates in the -:term:`cool` :term:`variety`. - - .. index:: single: error handling; varieties single: variety diff --git a/mps/manual/html/_sources/topic/plinth.txt b/mps/manual/html/_sources/topic/plinth.txt index fc50e847a0e..41866ed76e0 100644 --- a/mps/manual/html/_sources/topic/plinth.txt +++ b/mps/manual/html/_sources/topic/plinth.txt @@ -116,8 +116,8 @@ I/O module .. note:: - In the ANSI I/O module, ``mpsioan.c``, this calls ``fopen`` on - the file named by the environment variable + In the ANSI I/O module, ``mpsioan.c``, this calls + :c:func:`fopen` on the file named by the environment variable :envvar:`MPS_TELEMETRY_FILENAME`. @@ -134,7 +134,8 @@ I/O module .. note:: - In the ANSI I/O module, ``mpsioan.c``, this calls ``fclose``. + In the ANSI I/O module, ``mpsioan.c``, this calls + :c:func:`fclose`. .. c:function:: mps_res_t mps_io_write(mps_io_t io, void *buf, size_t size) @@ -153,7 +154,8 @@ I/O module .. note:: - In the ANSI I/O module, ``mpsioan.c``, this calls ``fwrite``. + In the ANSI I/O module, ``mpsioan.c``, this calls + :c:func:`fwrite`. .. c:function:: mps_res_t mps_io_flush(mps_io_t io) @@ -177,7 +179,8 @@ I/O module .. note:: - In the ANSI I/O module, ``mpsioan.c``, this calls ``fflush``. + In the ANSI I/O module, ``mpsioan.c``, this calls + :c:func:`fflush`. .. index:: @@ -246,9 +249,9 @@ Library module In the ANSI Library module, ``mpsliban.c``, this reports the failure using ``fprintf(stderr, "...%s...", message)`` and, in the :term:`cool` :term:`variety`, terminates the program by - calling ``abort``. You can change this behaviour with - :c:func:`mps_lib_assert_fail_install`. For a discussion of - the default behaviour, see :ref:`topic-error-assertion-handling`. + calling :c:func:`abort`. You can change this behaviour with + :c:func:`mps_lib_assert_fail_install`. For a discussion of the + default behaviour, see :ref:`topic-error-assertion-handling`. .. c:function:: extern mps_lib_assert_fail_t mps_lib_assert_fail_install(mps_lib_assert_fail_t handler) @@ -292,13 +295,13 @@ Library module :c:func:`mps_lib_get_EOF` if not. This function is intended to have the same semantics as the - ``fputc`` function of the ANSI C Standard (:ref:`ISO/IEC 9899:1990 - ` §7.11.7.3). + :c:func:`fputc` function of the ANSI C Standard (:ref:`ISO/IEC + 9899:1990 ` §7.11.7.3). .. note:: In the ANSI Library module, ``mpsliban.c``, this is a simple - wrapper around ``fputc``. + wrapper around :c:func:`fputc`. .. c:function:: int mps_lib_fputs(const char *s, mps_lib_FILE *stream) @@ -310,8 +313,8 @@ Library module ``stream`` is the stream. This function is intended to have the same semantics as the - ``fputs`` function of the ANSI C Standard (:ref:`ISO/IEC 9899:1990 - ` §7.11.7.4). + :c:func:`fputs` function of the ANSI C Standard (:ref:`ISO/IEC + 9899:1990 ` §7.11.7.4). Return a non-negative integer if successful, or :c:func:`mps_lib_get_EOF` if not. @@ -319,7 +322,7 @@ Library module .. note:: In the ANSI Library module, ``mpsliban.c``, this is a simple - wrapper around ``fputs``. + wrapper around :c:func:`fputs`. .. c:function:: int mps_lib_get_EOF(void) @@ -367,7 +370,7 @@ Library module .. c:function:: int mps_lib_memcmp(const void *s1, const void *s2, size_t n) A :term:`plinth` function similar to the standard :term:`C` - function ``memcmp``. + function :c:func:`memcmp`. ``s1`` and ``s2`` point to :term:`blocks` of memory to be compared. @@ -379,19 +382,19 @@ Library module equal to, or less than the block pointed to by ``s2``. This function is intended to have the same semantics as the - ``memcmp`` function of the ANSI C Standard (:ref:`ISO/IEC + :c:func:`memcmp` function of the ANSI C Standard (:ref:`ISO/IEC 9899:1990 ` §7.11.4.1). .. note:: In the ANSI Library module, ``mpsliban.c``, this is a simple - wrapper around ``memcmp``. + wrapper around :c:func:`memcmp`. .. c:function:: void *mps_lib_memcpy(void *dest, const void *source, size_t n) A :term:`plinth` function similar to the standard :term:`C` - function ``memcpy``. + function :c:func:`memcpy`. ``dest`` points to the destination. @@ -402,7 +405,7 @@ Library module Returns ``dest``. This function is intended to have the same semantics as the - ``memcpy`` function of the ANSI C Standard (:ref:`ISO/IEC + :c:func:`memcpy` function of the ANSI C Standard (:ref:`ISO/IEC 9899:1990 ` §7.11.2.1). The MPS never passes overlapping blocks to @@ -411,13 +414,13 @@ Library module .. note:: In the ANSI Library module, ``mpsliban.c``, this is a simple - wrapper around ``memcpy``. + wrapper around :c:func:`memcpy`. .. c:function:: void *mps_lib_memset(void *s, int c, size_t n) A :term:`plinth` function similar to the standard :term:`C` - function ``memset``. + function :c:func:`memset`. ``s`` points to the :term:`block` to fill with the byte ``c``. @@ -428,13 +431,13 @@ Library module Returns ``s``. This function is intended to have the same semantics as the - ``memset`` function of the ANSI C Standard (:ref:`ISO/IEC + :c:func:`memset` function of the ANSI C Standard (:ref:`ISO/IEC 9899:1990 ` §7.11.6.1). .. note:: In the ANSI Library module, ``mpsliban.c``, this is a simple - wrapper around ``memset``. + wrapper around :c:func:`memset`. .. note:: diff --git a/mps/manual/html/_sources/topic/root.txt b/mps/manual/html/_sources/topic/root.txt index 823996f0d0e..28e2d08ada3 100644 --- a/mps/manual/html/_sources/topic/root.txt +++ b/mps/manual/html/_sources/topic/root.txt @@ -37,15 +37,17 @@ You can register a root at any time by calling one of the no two roots may overlap (that is, each reference is :term:`fixed` by at most one root). Roots may be: -1. on the program's :term:`control stack`; +1. in :term:`registers`; -2. in the program's static data; +2. on the program's :term:`control stack`; -3. in :term:`heap` not managed by the MPS (provided that you destroy +3. in the program's static data; + +4. in :term:`heap` not managed by the MPS (provided that you destroy the root before freeing it; see :ref:`the Scheme interpreter's global symbol table ` for an example); -4. in :term:`manually managed ` pools +5. in :term:`manually managed ` pools (provided that you remove the root before freeing it). Roots must not be in memory that is subject to :term:`garbage @@ -99,7 +101,7 @@ scan it for references: table of :term:`tagged references`; 5. :c:func:`mps_root_create_reg` if the root consists of the - registers and control stack of a thread. See + :term:`registers` and :term:`control stack` of a thread. See :ref:`topic-root-thread` below. diff --git a/mps/manual/html/_static/logo.png b/mps/manual/html/_static/logo.png index 42756c08d7a..f60476aa6ca 100644 Binary files a/mps/manual/html/_static/logo.png and b/mps/manual/html/_static/logo.png differ diff --git a/mps/manual/html/_static/pygments.css b/mps/manual/html/_static/pygments.css index 1a14f2ae1ab..d79caa151c2 100644 --- a/mps/manual/html/_static/pygments.css +++ b/mps/manual/html/_static/pygments.css @@ -13,11 +13,11 @@ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #303030 } /* Generic.Output */ +.highlight .go { color: #333333 } /* Generic.Output */ .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0040D0 } /* Generic.Traceback */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ diff --git a/mps/manual/html/design/alloc-frame.html b/mps/manual/html/design/alloc-frame.html new file mode 100644 index 00000000000..51d1c2656d6 --- /dev/null +++ b/mps/manual/html/design/alloc-frame.html @@ -0,0 +1,612 @@ + + + + + + + + + + 1. Allocation frame protocol — Memory Pool System 1.111.0 documentation + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+

1. Allocation frame protocol

+
+

1.1. Introduction

+

.intro: This document explains the design of the support for +allocation frames in MPS.

+

.readership: This document is intended for any MM developer.

+

.overview: Allocation frames are used for implementing stack pools; +each stack frame corresponds to an allocation frame. Allocation frames +may also be suitable for implementing other sub-pool groupings, such +as generations and ramp allocation patterns.

+

.overview.ambition: We now believe this to be a design that loses +too many advantages of stack allocation for questionable gains. The +requirements are almost entirely based on unanalysed anecdote, instead +of actual clients.

+
+

Note

+

We plan to supersede this with a stack pool design at some point +in the future. Pekka P. Pirinen, 2000-03-09.

+
+
+
+

1.2. Definitions

+

.def.alloc-frame: An allocation frame is a generic name for a +device which groups objects together with other objects at allocation +time, and which may have a parent/child relationship with other +allocation frames.

+
+
+

1.3. Purpose

+

.purpose.stack-allocation: The allocation frame protocol is +intended to support efficient memory management for stack allocation, +that is, the allocation of objects which have dynamic extent.

+

.purpose.general: The allocation frame protocol is intended to be +sufficiently general that it will be useful in supporting other types +of nested allocation patterns too. For example, it could be used to +for EPVM-style save and restore, ramp allocation patterns or +generations.

+
+
+

1.4. Requirements

+
+

1.4.1. Known requirements

+

.req.stack-alloc: Provide a interface for clients to describe a +stack allocation pattern, as an alternative to using the control +stack.

+

.req.efficient: Permit an implementation which is comparable in +efficiency to allocating on the control stack.

+

.req.ap: Support allocation via allocation points (APs).

+

.req.format: Support the allocation of formatted objects.

+

.req.scan: Ensure that objects in allocation frames can participate +in garbage collection by being scanned.

+

.req.fix: Ensure that objects in allocation frames can participate +in garbage collection by accepting Fix requests.

+

.req.condemn: Ensure that objects in allocation frames can +participate in garbage collection by being condemned.

+

.attr.locking: Minimize the synchronization cost for the creation +and destruction of frames.

+
+
+

1.4.2. Proto-requirements

+

.proto-req: The following are possible requirements that might be +important in the future. The design does not necessarily meet all +these requirements, but it does consider them all. Each requirement +either has direct support in the framework, or could be supported with +future additions to the framework.

+

.req.parallels: The allocation frame protocol should provide a +framework for exploiting the parallels between stack extents, +generations and “ramps”.

+

.req.pool-destroy: It should be possible to use allocation frames +to free all objects in a pool without destroying the pool.

+

.req.epvm: It should be possible to implement EPVM-style save and +restore operations by creating and destroying allocation frames.

+

.req.subst: It should be possible to substitute a stack pool with a +GC-ed pool so that erroneous use of a stack pool can be detected.

+

.req.format-extensions: It should be possible for stack pools to +utilize the same format as any other pool, including debugging formats +that include fenceposting, etc.

+

.req.mis-nest: Should ensure “mis-nested” stacks are safe.

+

.req.non-top-level: Should support allocation in the non-top stack +extent.

+

.req.copy-if-necessary: Should ensure that stack pools can support +“copy-if-necessary” (so that low-level system code can heapify stack +objects.)

+

.req.preserve: When an object is in an allocation frame which is +being destroyed, it should be possible to preserve that object in the +parent frame.

+

.req.contained: Should allow clients to ask if an object is +“contained” in a frame. The object is contained in a frame if it is +affected when the frame is ended.

+

.req.alloc-with-other: Should allow clients to allocate an object +in the same frame as another object.

+
+
+
+

1.5. Overview

+

.frame-classes: The protocol supports different types of allocation +frames, which are represented as “frame classes”. It’s up to pools to +determine which classes of allocation frames they support. Pools which +support more than one frame class rely on the client to indicate which +class is currently of interest. The client indicates this by means of +an operation which stores the class in the buffer to which the +allocation point is attached.

+

.frame-handles: Allocation frames are described via abstract “frame +handles”. Pools may choose what the representation of a frame handle +should be. Frame handles are static, and the client need not store +them in a GC root.

+

.lightweight-frames: The design includes an extension to the +allocation point protocol, which permits the creation and destruction +of allocation frames without the necessity for claiming the arena +lock. Such frames are called “lightweight frames”.

+
+
+

1.6. Operations

+

.op.intro: Each operation has both an external (client) interface +and an internal (MPS) interface. The external function takes an +allocation point as a parameter, determines which buffer and pool it +belongs to, and calls the internal function with the buffer and pool +as parameters.

+

.op.obligatory: The following operations are supported on any +allocation point which supports allocation frames:-

+

.operation.push: The PushFrame() operation creates a new +allocation frame of the currently chosen frame class, makes this new +frame the current frame, and returns a handle for the frame.

+

.operation.pop: The PopFrame() operation takes a frame handle +as a parameter. Some pool classes might insist or assume that this is +the handle for the current frame. It finds the parent of that frame +and makes it the current frame. The operation indicates that all +children of the new current frame contain objects which are likely to +be dead. The reclaim policy is up to the pool; some classes might +insist or assume that the objects must be dead, and eagerly free them. +Note that this might introduce the possibility of leaving dangling +pointers elsewhere in the arena. If so, it’s up to the pool to decide +what to do about this.

+

.op.optional: The following operations are supported for some +allocation frames, but not all. Pools may choose to support some or +all of these operations for certain frame classes. An unsupported +operation will return a failure value:-

+

.operation.select: The SelectFrame() operation takes a frame +handle as a parameter and makes that frame the current frame. It does +not indicate that any children of the current frame contain objects +which are likely to be dead.

+

.operation.select-addr: The SelectFrameOfAddr() operation takes +an address as a parameter and makes the frame of that address the +current frame. It does not indicate that any children of the current +frame contain objects which are likely to be dead.

+

.operation.in-frame: The AddrInFrame() operation determines +whether the supplied address is the address of an object allocated in +the supplied frame, or any child of that frame.

+

.operation.set: The SetFrameClass() operation takes a frame +class and an allocation point as parameters, and makes that the +current frame class for the allocation point. The next PushFrame() +operation will create a new frame of that class.

+
+
+

1.7. Interface

+
+

1.7.1. External types

+

.type.client.frame-handle: Frame handles are defined as the abstract +type mps_frame_t.

+
+
+struct mps_frame_class_s *mps_frame_class_t
+
+ +

.type.client.frame-class: Frame classes are defined as an abstract +type.

+

.type.client.frame-class.access: Clients access frame classes by +means of dedicated functions for each frame class.

+
+
+

1.7.2. External functions

+

.fn.client.push: mps_ap_frame_push() is used by clients to +invoke the PushFrame() operation. For lightweight frames, this +might not invoke the corresponding internal function.

+

.fn.client.pop: mps_ap_frame_pop() is used by clients to invoke +the PopFrame() operation. For lightweight frames, this might not +invoke the corresponding internal function.

+
+
+mps_res_t mps_ap_frame_select(mps_ap_t buf, mps_frame_t frame)
+
+ +

.fn.client.select: This following function is used by clients to +invoke the SelectFrame() operation.

+
+
+mps_res_t mps_ap_frame_select_from_addr(mps_ap_t buf, mps_addr_t addr)
+
+ +

.fn.client.select-addr: This function is used by clients to invoke +the SelectFrameOfAddr() operation.

+
+
+mps_res_t mps_ap_addr_in_frame(mps_bool_t *inframe_o, mps_ap_t buf, mps_addr_t *addrref, mps_frame_t frame)
+
+ +

.fn.client.in-frame: This function is used by clients to invoke the +AddrInFrame() operation.

+
+
+mps_res_t mps_ap_set_frame_class(mps_ap_t buf, mps_frame_class_t class)
+
+ +

.fn.client.set: This function is used by clients to invoke the +SetFrameClass() operation.

+
+
+mps_frame_class_t mps_alloc_frame_class_stack(void)
+
+ +

.fn.client.stack-frame-class: This function is used by clients to +access the frame class used for simple stack allocation.

+
+
+

1.7.3. Internal types

+
+
+struct AllocFrameStruct *AllocFrame
+
+ +

.type.frame-handle: Frame handles are defined as an abstract type.

+
+
+struct AllocFrameClassStruct *AllocFrameClass
+
+ +

.type.frame-class: Frame classes are defined as an abstract type.

+
+
+Res (*PoolFramePushMethod)(AllocFrame *frameReturn, Pool pool, Buffer buf)
+
+ +

.fn.push: A pool method of this type is called (if needed) to +invoke the PushFrame() operation.

+
+
+Res (*PoolFramePopMethod)(Pool pool, Buffer buf, AllocFrame frame)
+
+ +

.fn.pop: A pool method of this type is called (if needed) +to invoke the PopFrame operation:

+
+
+Res (*PoolFrameSelectMethod)(Pool pool, Buffer buf, AllocFrame frame)
+
+ +

.fn.select: A pool method of this type is called to invoke the +SelectFrame() operation.

+
+
+Res (*PoolFrameSelectFromAddrMethod)(Pool pool, Buffer buf, Addr addr)
+
+ +

.fn.select-addr: A pool method of this type is called to invoke the +SelectFrameOfAddr() operation.

+
+
+Res (*PoolAddrInFrameMethod)(Bool *inframeReturn, Pool pool, Seg seg, Addr *addrref, AllocFrame frame)
+
+ +

.fn.in-frame: A pool method of this type is called to invoke the +AddrInFrame() operation.

+
+
+Res (*PoolSetFrameClassMethod)(Pool pool, Buffer buf, AllocFrameClass class)
+
+ +

.fn.set: A pool method of this type is called to invoke the +SetFrameClass() operation.

+
+
+
+

1.8. Lightweight frames

+
+

1.8.1. Overview

+

.lw-frame.overview: Allocation points provide direct support for +lightweight frames, and are designed to permit PushFrame and PopFrame +operations without the need for locking and delegation to the pool +method. Pools can disable this mechanism for any allocation point, so +that the pool method is always called. The pool method will be called +whenever synchronization is required for other reasons (e.g. the +buffer is tripped).

+

.lw-frame.model: Lightweight frames offer direct support for a +particular model of allocation frame use, whereby the PushFrame +operation returns the current allocation pointer as a frame handle, +and the PopFrame operation causes the allocation pointer to be reset +to the address of the frame handle. This model should be suitable for +simple stack frames, where more advanced operations like SelectFrame +are not supported. It may also be suitable for more advanced +allocation frame models when they are being used simply. The use of a +complex operation always involves synchronization via locking, and the +pool may disable lightweight synchronization temporarily at this time.

+
+
+

1.8.2. State

+

.lw-frame.states: Allocation points supporting lightweight frames +will be in one of the following states:

+
++++ + + + + + + + + + + + +
ValidIndicates that PushFrame() can be a lightweight +operation and need not be synchronized.
PopPendingIndicates that there has been a PopFrame() operation +that the pool must respond to.
DisabledIndicates that the pool has disabled support for lightweight +operations for this AP.
+

These states are in addition to the state normally held by an AP for +allocation purposes. An AP will be in the Disabled state at creation.

+

.lw-frame.transitions: State transitions happen under the following +circumstances:

+ ++++ + + + + + + + + + + + + + + + + + + + + +
Valid → PopPendingAs a result of a client PopFrame() +operation.
Valid → DisabledAt the choice of the pool (for example, when +responding to a SelectFrame() operation).
PopPending → ValidAt the choice of the pool, when processing a +PopFrame().
PopPending → DisabledAt the choice of the pool, when processing a +PopFrame().
Disabled → ValidAt the choice of the pool.
Disabled → PopframeIllegal.
+

.lw-frame.state-impl: Each AP contains 3 additional fields to hold this state:

+
mps_addr_t frameptr;
+mps_bool_t enabled;
+mps_bool_t lwPopPending;
+
+
+

.lw-frame.enabled: The enabled slot holds the following values for +each state:

+ ++++ + + + + + + + + + + + +
ValidTRUE
PopPendingTRUE
DisabledFALSE
+

.lw-frame.frameptr: The frameptr slot holds the following values +for each state:

+ ++++ + + + + + + + + + + + +
ValidNULL
PopPendingFrame handle for most recently popped frame.
DisabledNULL
+

.lw-frame.lwPopPending: The lwPopPending slot holds the +following values for each state:

+ ++++ + + + + + + + + + + + +
ValidFALSE
PopPendingTRUE
DisabledFALSE
+

.lw-frame.state-for-gc: It is not necessary for the tracer, format +code, pool, or any other part of the GC support in MPS to read either +of the two additional AP fields in order to scan a segment which +supports a lightweight allocation frame.

+ +
+

1.8.3. Synchronization

+

.lw-frame.sync: The purpose of the design is that mutator may +access the state of an AP without locking with MPS (via the external +functions). The design assumes the normal MPS restriction that an +operation on an AP may only be performed by a single mutator thread at +a time. Each of the operations on allocation frames counts as an +operation on an AP.

+

.lw-frame.sync.pool: Pools are permitted to read or modify the +lightweight frame state of an AP only in response to an operation on +that AP.

+

.lw-frame.sync.external: The external functions +mps_ap_frame_push() and mps_ap_frame_pop() are permitted to +read the values of the enabled and frameptr fields for the +supplied AP without claiming the arena lock. They are permitted to +modify the frameptr field if and only if enabled == FALSE.

+

.lw-frame.sync.trip: When a buffer trip happens, and the trap +wasn’t set by MPS itself (that is, it wasn’t because of a flip or for +logging), then the buffer code must check whether the AP has state +PopPending. If it does, the buffer code must call the Pool.

+
+
+

1.8.4. Implementation

+

.lw-frame.push: The external PushFrame() operation +(mps_ap_frame_push()) performs the following operations:

+
IF (!APIsTrapped(ap) && StateOfFrame(ap) == Valid && ap->init == ap->alloc)
+   *frame_o = ap->init;
+ELSE
+  WITH_ARENA_LOCK
+    PerformInternalPushFrameOperation(...)
+  END
+END
+
+
+

.lw-frame.pop: The external PopFrame() operation +(mps_ap_frame_pop()) performs the following operations:

+
IF (StateOfFrame(ap) != Disabled)
+  TrapAP(ap);  /* ensure next allocation or push involves the pool */
+  ap->frameptr = frame;
+  ap->lwpopPending = TRUE;
+ELSE
+  WITH_ARENA_LOCK
+    PerformInternalPopFrameOperation(...)
+  END
+END
+
+
+
+ + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/mps/manual/html/design/arena.html b/mps/manual/html/design/arena.html index 7a9f51f18cb..3ddabd37c0f 100644 --- a/mps/manual/html/design/arena.html +++ b/mps/manual/html/design/arena.html @@ -8,7 +8,7 @@ - 1. Arena — Memory Pool System 1.111.0 documentation + 2. Arena — Memory Pool System 1.111.0 documentation @@ -28,8 +28,8 @@ - - + +