From e07d0badc8aa935317217386e48cd2336bccf885 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 16 Oct 2012 23:28:24 +0100 Subject: [PATCH] Add description of the object format to the language guide. Copied from Perforce Change: 179906 ServerID: perforce.ravenbrook.com --- mps/manual/source/glossary/c.rst | 4 +- mps/manual/source/glossary/f.rst | 18 +- mps/manual/source/glossary/i.rst | 2 +- mps/manual/source/glossary/p.rst | 2 +- mps/manual/source/guide/lang.rst | 462 +++++++++++++++++++++++- mps/manual/source/guide/scheme-after.c | 137 +++---- mps/manual/source/guide/scheme-before.c | 46 ++- mps/manual/source/notes.txt | 116 ++++-- mps/manual/source/reference/index.rst | 14 +- 9 files changed, 659 insertions(+), 142 deletions(-) diff --git a/mps/manual/source/glossary/c.rst b/mps/manual/source/glossary/c.rst index 67ade635806..b6ccb9646f4 100644 --- a/mps/manual/source/glossary/c.rst +++ b/mps/manual/source/glossary/c.rst @@ -204,8 +204,8 @@ Memory Management Glossary: C A :term:`formatted object` that contains data from the :term:`client program`. One of three types of formatted - objects, the other two being :term:`forwarded objects - ` and :term:`padding objects ` and :term:`padding objects `. client program diff --git a/mps/manual/source/glossary/f.rst b/mps/manual/source/glossary/f.rst index eba5b1f1961..ef5b5df816e 100644 --- a/mps/manual/source/glossary/f.rst +++ b/mps/manual/source/glossary/f.rst @@ -272,16 +272,8 @@ Memory Management Glossary: F object with a :term:`forwarding marker` that points to the new location of the object. See :c:type:`mps_fmt_fwd_t`. - forwarded object - - .. mps:specific:: - - A :term:`formatted object` that has been replaced by a - :term:`forwarding marker`. One of three types of formatted - objects, the other two being :term:`client objects ` and :term:`padding objects `. - forwarding marker + forwarding object forwarding pointer Some :term:`garbage collectors ` @@ -295,7 +287,13 @@ Memory Management Glossary: F .. seealso:: :term:`copying garbage collection`, :term:`two-space collector`. - .. mps:specific:: The term *forwarding marker* is used. + .. mps:specific:: + + The term *forwarding object* is used. This is a + :term:`formatted object` that has been replaced by a + :term:`forwarding marker`. One of three types of formatted + objects, the other two being :term:`client objects ` and :term:`padding objects `. fragmentation diff --git a/mps/manual/source/glossary/i.rst b/mps/manual/source/glossary/i.rst index e0c609d0ee4..a45047b11cb 100644 --- a/mps/manual/source/glossary/i.rst +++ b/mps/manual/source/glossary/i.rst @@ -345,6 +345,6 @@ Memory Management Glossary: I A :term:`format method` that is called by a :term:`moving ` :term:`pool ` to determine if a :term:`formatted object` is a - :term:`forwarded object`, and if so, to return the address + :term:`forwarding object`, and if so, to return the address where the object was moved to. See :c:type:`mps_fmt_isfwd_t`. diff --git a/mps/manual/source/glossary/p.rst b/mps/manual/source/glossary/p.rst index 5c207295a94..5f79d5d964c 100644 --- a/mps/manual/source/glossary/p.rst +++ b/mps/manual/source/glossary/p.rst @@ -32,7 +32,7 @@ Memory Management Glossary: P A :term:`formatted object` that consists of :term:`padding`. One of three types of formatted objects, the other two being :term:`client objects ` - and :term:`forwarded objects `. + and :term:`forwarding objects `. page diff --git a/mps/manual/source/guide/lang.rst b/mps/manual/source/guide/lang.rst index 92f5c23835b..7a514293b4b 100644 --- a/mps/manual/source/guide/lang.rst +++ b/mps/manual/source/guide/lang.rst @@ -16,7 +16,7 @@ garbage collection to the runtime system for a programming language. I'm assuming that you've downloaded and compiled the MPS (see :ref:`guide-install`), and that you are familiar with the overall -architecture of the MPS (:ref:`guide-overview`). +architecture of the MPS (see :ref:`guide-overview`). ------------------ @@ -94,12 +94,23 @@ operate on objects generically, testing ``TYPE(obj)`` as necessary. For example, } } -Each constructor (for example ``make_pair`` is the constructor for -pairs) allocates memory for the new object by calling ``malloc``, -but objects do not get freed, because they have :term:`indefinite -extent`, so it is necessary to prove that they are :term:`dead` before -their memory can be :term:`reclaimed `. And that task falls -to the :term:`garbage collector`. +Each constructor allocates memory for the new object by calling +``malloc``. For example ``make_pair`` is the constructor for pairs:: + + static obj_t make_pair(obj_t car, obj_t cdr) + { + obj_t obj = (obj_t)malloc(sizeof(pair_s)); + if (obj == NULL) error("out of memory"); + total += sizeof(pair_s); + obj->pair.type = TYPE_PAIR; + CAR(obj) = car; + CDR(obj) = cdr; + return obj; + } + +Objects do not get freed, because it is necessary to prove that they +are :term:`dead` before their memory can be :term:`reclaimed +`. And that task falls to the :term:`garbage collector`. ----------------------- @@ -191,21 +202,20 @@ these features of the MPS. classes. ------------------ -The object format ------------------ +----------------------- +Describing your objects +----------------------- -In order for the MPS to be able to collect your objects, you need to +In order for the MPS to be able to manage your objects, you need to tell it how to perform various operations on those objects, which you -do by creating a set of :term:`format method `, and -using them to specify an :term:`object format`. Here's the code for -creating the object format for the Scheme interpreter:: +do by creating an :term:`object format`. Here's the code for creating +the object format for the Scheme interpreter:: struct mps_fmt_A_s obj_fmt_s = { sizeof(mps_word_t), obj_scan, obj_skip, - obj_copy, + NULL, obj_fwd, obj_isfwd, obj_pad, @@ -223,7 +233,8 @@ belonging to this format. The Scheme interpreter needs its objects to be allocated at addresses which are multiples of the machine's word size. -The other elements of the structure are the format methods, which are described in the following sections. +The other elements of the structure are the :term:`format methods +`, which are described in the following sections. .. topics:: @@ -234,11 +245,424 @@ The other elements of the structure are the format methods, which are described The scan method --------------- +The :term:`scan method` is a function of type +:c:type:`mps_fmt_scan_t`. It is called by the MPS to :term:`scan` a +block of memory. Its task is to identify all references within the +objects in the block of memory, and "fix" them, by calling the macros +:c:func:`MPS_FIX1` and :c:func:`MPS_FIX2` on each reference (usually +via the convenience macro :c:func:`MPS_FIX12`). + +"Fixing" is a generic operation whose effect depends on the context in +which the scan method was called. The scan method is called to +discover references and so determine which objects are :term:`alive` +and which are :term:`dead`, and also to update references after +objects have been moved. + +Here's the scan method for the Scheme example:: + + static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) + { + MPS_SCAN_BEGIN(ss) { + while (base < limit) { + obj_t obj = base; + switch (obj->type.type) { + case TYPE_PAIR: + FIX(obj->pair.car); + FIX(obj->pair.cdr); + base = (char *)base + ALIGN(sizeof(pair_s)); + break; + case TYPE_INTEGER: + base = (char *)base + ALIGN(sizeof(integer_s)); + break; + /* ... and so on for the other types ... */ + default: + fprintf(stderr, "Unexpected object on the heap\n"); + abort(); + } + } + } MPS_SCAN_END(ss); + return MPS_RES_OK; + } + +The scan method receives a :term:`scan state` (``ss``) argument, and +the block of memory to scan, from ``base`` (inclusive) to ``limit`` +(exclusive). This block of memory is known to be packed with objects +belonging to the object format, and so the scan method loops over the +objects in the block, dispatching on the type of each object, and then +updating ``base`` to point to the next object in the block. + +For each reference in an object the scan method fixes it by calling +:c:func:`MPS_FIX12` via the macro ``FIX``, which is defined as +follows:: + + #define FIX(ref) \ + do { \ + mps_addr_t _addr = (ref); /* copy to local to avoid type pun */ \ + mps_res_t res = MPS_FIX12(ss, &_addr); \ + if (res != MPS_RES_OK) return res; \ + (ref) = _addr; \ + } while(0) + +Each call to :c:func:`MPS_FIX12` must appear between calls to the +macros :c:func:`MPS_SCAN_BEGIN` and :c:func:`MPS_SCAN_END`. It's +usually most convenient to call :c:func:`MPS_SCAN_BEGIN` at the start +of the function and :c:func:`MPS_SCAN_END` at the end. + +There are a few things to watch out for: + +1. When the MPS calls your scan method, it may be part-way through + moving your objects. It is therefore essential that the scan method + only examine objects in the range of addresses it is given. Objects + in other ranges of addresses are not guaranteed to be in a + consistent state. + +2. Scanning is an operation on the :term:`critical path` of the MPS, + which means that it is important that it runs as quickly as + possible. + +3. The "fix" operation may update the reference. So if your language + has :term:`tagged references `, you must make + sure that the tag is restored after the reference is updated. + +4. The "fix" operation may fail by returning a :term:`result code` + other than :c:macro:`MPS_RES_OK`. A scan function must propagate + such a result code to the caller, and should do so as soon as + practicable. + +.. topics:: + + :ref:`topic-format`, :ref:`topic-scanning`. + + +--------------- +The skip method +--------------- + +The :term:`skip method` is a function of type +:c:type:`mps_fmt_skip_t`. It is called by the MPS to skip over an +object belonging to the format. + +Here's the skip method for the Scheme example:: + + static mps_addr_t obj_skip(mps_addr_t base) + { + obj_t obj = base; + switch (obj->type.type) { + case TYPE_PAIR: + base = (char *)base + ALIGN(sizeof(pair_s)); + break; + case TYPE_INTEGER: + base = (char *)base + ALIGN(sizeof(integer_s)); + break; + /* ... and so on for the other types ... */ + default: + fprintf(stderr, "Unexpected object on the heap\n"); + abort(); + } + return base; + } + +The argument ``base`` is the address to the base of the object. The +skip method must return the address of the base of the "next object": +in formats of variant A like this one, this is the address of the word +just past the end of the object. + +You'll see that the code in the skip method that computes the "next +object" is the same as the corresponding code in the :term:`scan +method`, so that it's tempting for the latter to delegate this part of +its functionality to the former. + +.. topics:: + + :ref:`topic-format`, :ref:`topic-scanning`. + + +------------------ +The forward method +------------------ + +The :term:`forward method` is a function of type +:c:type:`mps_fmt_fwd_t`. It is called by the MPS after it has moved an +object, and its task is to replace the old object with a +:term:`forwarding object` pointing to the new location of the object. + +The forwarding object must satisfy these properties: + +1. It must be scannable and skippable, and so it will need to have a + type field to distinguish it from other Scheme objects. + +2. It must contain a pointer to the new location of the object (a + :term:`forwarding pointer`). + +3. The scan method and the skip method will both need to know the + length of the forwarding object. This can be arbitarily long (in + the case of string objects, for example) so it must contain a + length field. + +This poses a problem, because the above analysis suggests that +forwarding objects need to contain at least three words, but Scheme +objects might be as small as two words (for example, integers). + +This conundrum can be solved by having two types of forwarding object. +The first type is suitable for forwarding objects of three words or +longer:: + + typedef struct fwd_s { + type_t type; /* TYPE_FWD */ + obj_t fwd; /* forwarded object */ + size_t size; /* total size of this object */ + } fwd_s; + +while the second type is suitable for forwarding objects of two words:: + + typedef struct fwd2_s { + type_t type; /* TYPE_FWD2 */ + obj_t fwd; /* forwarded object */ + } fwd2_s; + +Here's the forward method for the Scheme example:: + + static void obj_fwd(mps_addr_t old, mps_addr_t new) + { + obj_t obj = old; + mps_addr_t limit = obj_skip(old); + size_t size = (char *)limit - (char *)old; + assert(size >= ALIGN(sizeof(fwd2_s))); + if (size == ALIGN(sizeof(fwd2_s))) { + obj->type.type = TYPE_FWD2; + obj->fwd2.fwd = new; + } else { + obj->type.type = TYPE_FWD; + obj->fwd.fwd = new; + obj->fwd.size = size; + } + } + +The argument ``old`` is the old address of the object, and ``new`` is +the location to which it has been moved. + +The fowarding objects must be scannable and skippable, so the +following code must be added to ``obj_scan`` and ``obj_skip``:: + + case TYPE_FWD: + base = (char *)base + ALIGN(obj->fwd.size); + break; + case TYPE_FWD2: + base = (char *)base + ALIGN(sizeof(fwd2_s)); + break; + +.. note:: + + The Scheme interpreter has no objects consisting of a single word. + If it did, the type and the forwarding pointer would have to be + combined into a forwarding object consisting of a single word, for + example by relying on the fact that pointers are never equal to + the small integers that are used to represent types. + +.. topics:: + + :ref:`topic-format`. + + +----------------------- +The is-forwarded method +----------------------- + +The :term:`is-forwarded method` is a function of type +:c:type:`mps_fmt_isfwd_t`. It is called by the MPS to determine if an +object is a :term:`forwarding object`, and if it is, to determine the +location where that object was moved. + +Here's the is-forwarded method for the Scheme example:: + + static mps_addr_t obj_isfwd(mps_addr_t addr) + { + obj_t obj = addr; + switch (obj->type.type) { + case TYPE_FWD2: + return obj->fwd2.fwd; + case TYPE_FWD: + return obj->fwd.fwd; + } + return NULL; + } + +It receives the address of an object, and returns the address to which +that object was moved, or ``NULL`` if the object was not moved. + +.. topics:: + + :ref:`topic-format`. + + +------------------ +The padding method +------------------ + +The :term:`padding method` is a function of type +:c:type:`mps_fmt_pad_t`. It is called by the MPS to fill a block of +memory with a :term:`padding object`: this is an object that fills +gaps in a block of :term:`formatted objects `, for +example to enable the MPS to pack objects into fixed-size units (such +as operating system :term:`pages `). + +A padding object must be scannable and skippable, and not confusable +with a :term:`forwarding object`. This means they need a type and a +size. However, padding objects might need to be as small as the +alignment of the object format, which was specified to be a single +word. As with forwarding objects, this can be solved by having two +types of padding object. The first type is suitable for paddding +objects of two words or longer:: + + typedef struct pad_s { + type_t type; /* TYPE_PAD */ + size_t size; /* total size of this object */ + } pad_s; + +while the second type is suitable for padding objects consisting of a +single word:: + + typedef struct pad1_s { + type_t type; /* TYPE_PAD1 */ + } pad1_s; + +Here's the padding method:: + + static void obj_pad(mps_addr_t addr, size_t size) + { + obj_t obj = addr; + assert(size >= ALIGN(sizeof(pad1_s))); + if (size == ALIGN(sizeof(pad1_s))) { + obj->type.type = TYPE_PAD1; + } else { + obj->type.type = TYPE_PAD; + obj->pad.size = size; + } + } + +The argument ``addr`` is the address at which the padding object must be created, and ``size`` is its size in bytes: this will always be a multiple of the alignment of the object format. + +The padding objects must be scannable and skippable, so the following +code must be added to ``obj_scan`` and ``obj_skip``:: + + case TYPE_PAD: + base = (char *)base + ALIGN(obj->pad.size); + break; + case TYPE_PAD1: + base = (char *)base + ALIGN(sizeof(pad1_s)); + break; + +.. topics:: + + :ref:`topic-format`. + + +----------------- +Generation chains +----------------- + +The AMC pool requires not only an object format but a +:term:`generation chain`. This specifies the generation structure of +the :term:`generational garbage collection`. + +The client program creates a generation chain by constructing an array +of structures of type :c:type:`mps_gen_param_s` and passing them to +:c:func:`mps_chain_create`. Here's the code for creating +the generation chain for the Scheme interpreter:: + + mps_gen_param_s obj_gen_params[] = { + { 150, 0.85 }, + { 170, 0.45 }, + }; + + res = mps_chain_create(&obj_chain, + arena, + LENGTH(obj_gen_params), + obj_gen_params); + if (res != MPS_RES_OK) error("Couldn't create obj chain"); + + +----------------- +Creating the pool +----------------- + +Now you know enough to create an AMC pool! Let's review the pool +creation code. First the object format:: + + struct mps_fmt_A_s obj_fmt_s = { + sizeof(mps_word_t), + obj_scan, + obj_skip, + NULL, + obj_fwd, + obj_isfwd, + obj_pad, + }; + + mps_fmt_t obj_fmt; + res = mps_fmt_create_A(&obj_fmt, arena, &obj_fmt_s); + if (res != MPS_RES_OK) error("Couldn't create obj format"); + +then the generation chain:: + + mps_gen_param_s obj_gen_params[] = { + { 150, 0.85 }, + { 170, 0.45 }, + }; + + mps_chain_t obj_chain; + res = mps_chain_create(&obj_chain, + arena, + LENGTH(obj_gen_params), + obj_gen_params); + if (res != MPS_RES_OK) error("Couldn't create obj chain"); + +and finally the pool:: + + mps_pool_t obj_pool; + res = mps_pool_create(&obj_pool, + arena, + mps_class_amc(), + obj_fmt, + obj_chain); + if (res != MPS_RES_OK) error("Couldn't create obj pool"); + + +----- +Roots +----- --------------- -The trampoline --------------- +.. topics:: + + :ref:`topic-root`. +------- +Threads +------- + + + + +---------- +Allocation +---------- + + + +.. topics:: + + :ref:`topic-allocation`. + + + +-------- +Messages +-------- + + +.. topics:: + + :ref:`topic-message`. diff --git a/mps/manual/source/guide/scheme-after.c b/mps/manual/source/guide/scheme-after.c index c6235d2b16d..fd9c0e7d3a6 100644 --- a/mps/manual/source/guide/scheme-after.c +++ b/mps/manual/source/guide/scheme-after.c @@ -16,7 +16,7 @@ * (define (church n f a) (if (eqv? n 0) a (church (- n 1) f (f a)))) * (church 1000 triangle 0) * - * This won't produce interesting results but it will cause a garbage + * This won't produce interesting results but it will cause garbage * collection cycles. Note that there's never any waiting for the MPS. * THAT'S THE POINT. * @@ -109,9 +109,10 @@ enum { TYPE_PROMISE, TYPE_CHARACTER, TYPE_VECTOR, - TYPE_FWD2, /* two-word broken heart */ - TYPE_FWD, /* three-words and up broken heart */ - TYPE_PAD1 /* one-word padding object */ + TYPE_FWD, /* three words and up forwarding object */ + TYPE_FWD2, /* two-word forwarding object */ + TYPE_PAD, /* two words and up padding object */ + TYPE_PAD1, /* one-word padding object */ }; typedef struct type_s { @@ -193,17 +194,22 @@ typedef struct vector_s { * See obj_pad, obj_fwd etc. to see how these are used. */ -typedef struct fwd2_s { - type_t type; /* TYPE_FWD2 */ - obj_t fwd; /* forwarded object */ -} fwd2_s; - typedef struct fwd_s { type_t type; /* TYPE_FWD */ obj_t fwd; /* forwarded object */ size_t size; /* total size of this object */ } fwd_s; +typedef struct fwd2_s { + type_t type; /* TYPE_FWD2 */ + obj_t fwd; /* forwarded object */ +} fwd2_s; + +typedef struct pad_s { + type_t type; /* TYPE_PAD */ + size_t size; /* total size of this object */ +} pad_s; + typedef struct pad1_s { type_t type; /* TYPE_PAD1 */ } pad1_s; @@ -222,6 +228,7 @@ typedef union obj_u { vector_s vector; fwd2_s fwd2; fwd_s fwd; + pad_s pad; } obj_s; @@ -317,7 +324,7 @@ static obj_t obj_unquote_splic; /* "unquote-splicing" symbol */ * be decoded by enclosing code.] */ -static jmp_buf *error_handler; +static jmp_buf *error_handler = NULL; static char error_message[MSGMAX+1]; @@ -363,13 +370,17 @@ static void error(char *format, ...) { va_list args; - assert(error_handler != NULL); - va_start(args, format); - vsprintf(error_message, format, args); + vsnprintf(error_message, sizeof error_message, format, args); va_end(args); - longjmp(*error_handler, 1); + if (error_handler) { + longjmp(*error_handler, 1); + } else { + fprintf(stderr, "Fatal error during initialization: %s\n", + error_message); + abort(); + } } @@ -826,8 +837,8 @@ static void print(obj_t obj, unsigned depth, FILE *stream) } break; default: - assert(0); - abort(); + assert(0); + abort(); } } @@ -2351,33 +2362,42 @@ static obj_t entry_gc(obj_t env, obj_t op_env, obj_t operator, obj_t operands) /* special table */ -static struct {char *name; obj_t *varp;} sptab[] = { +static struct { + char *name; + obj_t *varp; +} sptab[] = { {"()", &obj_empty}, {"#[eof]", &obj_eof}, {"#[error]", &obj_error}, {"#t", &obj_true}, {"#f", &obj_false}, {"#[undefined]", &obj_undefined}, - {"#[tail]", &obj_tail} + {"#[tail]", &obj_tail}, }; /* initial symbol table */ -static struct {char *name; obj_t *varp;} isymtab[] = { +static struct { + char *name; + obj_t *varp; +} isymtab[] = { {"quote", &obj_quote}, {"lambda", &obj_lambda}, {"begin", &obj_begin}, {"else", &obj_else}, {"quasiquote", &obj_quasiquote}, {"unquote", &obj_unquote}, - {"unquote-splicing", &obj_unquote_splic} + {"unquote-splicing", &obj_unquote_splic}, }; /* operator table */ -static struct {char *name; entry_t entry;} optab[] = { +static struct { + char *name; + entry_t entry; +} optab[] = { {"quote", entry_quote}, {"define", entry_define}, {"set!", entry_set}, @@ -2392,13 +2412,16 @@ static struct {char *name; entry_t entry;} optab[] = { {"letrec", entry_letrec}, {"do", entry_do}, {"delay", entry_delay}, - {"quasiquote", entry_quasiquote} + {"quasiquote", entry_quasiquote}, }; /* function table */ -static struct {char *name; entry_t entry;} funtab[] = { +static struct { + char *name; + entry_t entry; +} funtab[] = { {"not", entry_not}, {"boolean?", entry_booleanp}, {"eqv?", entry_eqvp}, @@ -2442,7 +2465,7 @@ static struct {char *name; entry_t entry;} funtab[] = { {"eval", entry_eval}, {"symbol->string", entry_symbol_to_string}, {"string->symbol", entry_string_to_symbol}, - {"gc", entry_gc} + {"gc", entry_gc}, }; @@ -2475,12 +2498,12 @@ static struct {char *name; entry_t entry;} funtab[] = { static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) { -#define FIX(ref) \ - do { \ +#define FIX(ref) \ + do { \ mps_addr_t _addr = (ref); /* copy to local to avoid type pun */ \ - mps_res_t res = MPS_FIX12(ss, &_addr); \ - if (res != MPS_RES_OK) return res; \ - (ref) = _addr; \ + mps_res_t res = MPS_FIX12(ss, &_addr); \ + if (res != MPS_RES_OK) return res; \ + (ref) = _addr; \ } while(0) MPS_SCAN_BEGIN(ss) { @@ -2530,20 +2553,21 @@ static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) ALIGN(offsetof(vector_s, vector) + obj->vector.length * sizeof(obj->vector.vector[0])); break; + case TYPE_FWD: + base = (char *)base + ALIGN(obj->fwd.size); + break; case TYPE_FWD2: base = (char *)base + ALIGN(sizeof(fwd2_s)); break; - case TYPE_FWD: - base = (char *)base + ALIGN(obj->fwd.size); + case TYPE_PAD: + base = (char *)base + ALIGN(obj->pad.size); break; case TYPE_PAD1: base = (char *)base + ALIGN(sizeof(pad1_s)); break; default: - assert(0); fprintf(stderr, "Unexpected object on the heap\n"); abort(); - return MPS_RES_FAIL; } } } MPS_SCAN_END(ss); @@ -2594,20 +2618,21 @@ static mps_addr_t obj_skip(mps_addr_t base) ALIGN(offsetof(vector_s, vector) + obj->vector.length * sizeof(obj->vector.vector[0])); break; + case TYPE_FWD: + base = (char *)base + ALIGN(obj->fwd.size); + break; case TYPE_FWD2: base = (char *)base + ALIGN(sizeof(fwd2_s)); break; - case TYPE_FWD: - base = (char *)base + ALIGN(obj->fwd.size); + case TYPE_PAD: + base = (char *)base + ALIGN(obj->pad.size); break; case TYPE_PAD1: base = (char *)base + ALIGN(sizeof(pad1_s)); break; default: - assert(0); fprintf(stderr, "Unexpected object on the heap\n"); abort(); - return NULL; } return base; } @@ -2625,10 +2650,10 @@ static mps_addr_t obj_isfwd(mps_addr_t addr) { obj_t obj = addr; switch (obj->type.type) { - case TYPE_FWD2: - return obj->fwd2.fwd; case TYPE_FWD: return obj->fwd.fwd; + case TYPE_FWD2: + return obj->fwd2.fwd; } return NULL; } @@ -2663,10 +2688,10 @@ static void obj_fwd(mps_addr_t old, mps_addr_t new) * * The job of `obj_pad` is to fill in a block of memory with a padding * object that will be skipped by `obj_scan` or `obj_skip` but does - * nothing else. Because we've chosen to align to single words, we may + * nothing else. Because we've chosen to align to single words, we may * have to pad a single word, so we have a special single-word padding - * object, `PAD1` for that purpose. Otherwise we can use forwarding - * objects with their `fwd` fields set to `NULL`. + * object, `PAD1` for that purpose. Otherwise we can use multi-word + * padding objects, `PAD`. */ static void obj_pad(mps_addr_t addr, size_t size) @@ -2675,31 +2700,13 @@ static void obj_pad(mps_addr_t addr, size_t size) assert(size >= ALIGN(sizeof(pad1_s))); if (size == ALIGN(sizeof(pad1_s))) { obj->type.type = TYPE_PAD1; - } else if (size == ALIGN(sizeof(fwd2_s))) { - obj->type.type = TYPE_FWD2; - obj->fwd2.fwd = NULL; } else { - obj->type.type = TYPE_FWD; - obj->fwd.fwd = NULL; - obj->fwd.size = size; + obj->type.type = TYPE_PAD; + obj->pad.size = size; } } -/* obj_copy -- object format copy method %%MPS - * - * The job of `obj_copy` is to make a copy of an object. - * TODO: Explain why this exists. - */ - -static void obj_copy(mps_addr_t old, mps_addr_t new) -{ - mps_addr_t limit = obj_skip(old); - size_t size = (char *)limit - (char *)old; - (void)memcpy(new, old, size); -} - - /* obj_fmt_s -- object format parameter structure %%MPS * * This is simply a gathering of the object format methods and the chosen @@ -2710,10 +2717,10 @@ struct mps_fmt_A_s obj_fmt_s = { sizeof(mps_word_t), obj_scan, obj_skip, - obj_copy, + NULL, obj_fwd, obj_isfwd, - obj_pad + obj_pad, }; @@ -2908,7 +2915,7 @@ static void *start(void *p, size_t s) static mps_gen_param_s obj_gen_params[] = { { 150, 0.85 }, - { 170, 0.45 } + { 170, 0.45 }, }; diff --git a/mps/manual/source/guide/scheme-before.c b/mps/manual/source/guide/scheme-before.c index 3042d49632c..b42672230bc 100644 --- a/mps/manual/source/guide/scheme-before.c +++ b/mps/manual/source/guide/scheme-before.c @@ -227,7 +227,7 @@ static obj_t obj_unquote_splic; /* "unquote-splicing" symbol */ * be decoded by enclosing code.] */ -static jmp_buf *error_handler; +static jmp_buf *error_handler = NULL; static char error_message[MSGMAX+1]; @@ -247,13 +247,17 @@ static void error(char *format, ...) { va_list args; - assert(error_handler != NULL); - va_start(args, format); - vsprintf(error_message, format, args); + vsnprintf(error_message, sizeof error_message, format, args); va_end(args); - longjmp(*error_handler, 1); + if (error_handler) { + longjmp(*error_handler, 1); + } else { + fprintf(stderr, "Fatal error during initialization: %s\n", + error_message); + abort(); + } } @@ -625,8 +629,8 @@ static void print(obj_t obj, unsigned depth, FILE *stream) } break; default: - assert(0); - abort(); + assert(0); + abort(); } } @@ -2059,32 +2063,41 @@ static obj_t entry_string_to_symbol(obj_t env, obj_t op_env, obj_t operator, obj /* special table */ -static struct {char *name; obj_t *varp;} sptab[] = { +static struct { + char *name; + obj_t *varp; +} sptab[] = { {"()", &obj_empty}, {"#[eof]", &obj_eof}, {"#[error]", &obj_error}, {"#t", &obj_true}, {"#f", &obj_false}, - {"#[undefined]", &obj_undefined} + {"#[undefined]", &obj_undefined}, }; /* initial symbol table */ -static struct {char *name; obj_t *varp;} isymtab[] = { +static struct { + char *name; + obj_t *varp; +} isymtab[] = { {"quote", &obj_quote}, {"lambda", &obj_lambda}, {"begin", &obj_begin}, {"else", &obj_else}, {"quasiquote", &obj_quasiquote}, {"unquote", &obj_unquote}, - {"unquote-splicing", &obj_unquote_splic} + {"unquote-splicing", &obj_unquote_splic}, }; /* operator table */ -static struct {char *name; entry_t entry;} optab[] = { +static struct { + char *name; + entry_t entry; +} optab[] = { {"quote", entry_quote}, {"define", entry_define}, {"set!", entry_set}, @@ -2099,13 +2112,16 @@ static struct {char *name; entry_t entry;} optab[] = { {"letrec", entry_letrec}, {"do", entry_do}, {"delay", entry_delay}, - {"quasiquote", entry_quasiquote} + {"quasiquote", entry_quasiquote}, }; /* function table */ -static struct {char *name; entry_t entry;} funtab[] = { +static struct { + char *name; + entry_t entry; +} funtab[] = { {"not", entry_not}, {"boolean?", entry_booleanp}, {"eqv?", entry_eqvp}, @@ -2146,7 +2162,7 @@ static struct {char *name; entry_t entry;} funtab[] = { {"vector-fill!", entry_vector_fill}, {"eval", entry_eval}, {"symbol->string", entry_symbol_to_string}, - {"string->symbol", entry_string_to_symbol} + {"string->symbol", entry_string_to_symbol}, }; diff --git a/mps/manual/source/notes.txt b/mps/manual/source/notes.txt index d454577eb2f..1d0e2eb8c9a 100644 --- a/mps/manual/source/notes.txt +++ b/mps/manual/source/notes.txt @@ -1,19 +1,20 @@ TODO -2. Create new types of objects for pools and topics. - -3. Glossary entries need permalinks. See - - -5. Fix the general index so that (1) aren't interpreted as subentries. - -7. Re-do the diagrams in vector form and using the colour palette. - -8. Use a better bibliography extension. See for example - - -9. Hyphenate long function names across line endings (but what if you - copy them?) + 2. Create new types of objects for pools and topics. + + 3. Glossary entries need permalinks. See + + + 5. Fix the general index so that (1) aren't interpreted as + subentries. + + 7. Re-do the diagrams in vector form and using the colour palette. + + 8. Use a better bibliography extension. See for example + + + 9. Hyphenate long function names across line endings (but what if you + copy them?) 11. Support MMREF-style anchors to the glossary (#garbage.collection instead of #garbage-collection). @@ -21,17 +22,88 @@ TODO QUERIES -1. Does the object format description (struct mps_fmt_A_s) have to be - static? + 1. Does the object format description (struct mps_fmt_A_s etc) have + to be static? Is it OK to throw it away after calling + mps_fmt_create? -2. What is the difference, if any, between mps_word_t and MPS_T_WORD? + Same question for the generation structure (mps_gen_param_s)? + + 2. What is the difference, if any, between mps_word_t and MPS_T_WORD? + + 3. How can I explain why the Scheme example uses sizeof(mps_word_t) + as its alignment? Why not MPS_PF_ALIGN (or are client programs not + supposed to look at mpstd.h)? Why not something of its own + manufacture, like sizeof(union {long, size_t, void*})? + + 4. Why does the Scheme example have a copy method in its object + format when the reference manual says it's obsolete? -3. How can I explain why the Scheme example uses sizeof(mps_word_t) as - its alignment? Why not MPS_PF_ALIGN? Why not something of its own - manufacture, like sizeof(union {long, size_t, void*})? + I removed it. + + 5. What is the difference between the "event stream" and the + "telemetry stream"? Are these names for the same thing? Or is + there a distinction (for example, "event stream" refers to the + internal, unfiltered, stream of events and "telemetry stream" + refers to the filtered stream)? + + 6. The location dependency functions all take an arena as an + argument. What is the role of this argument? + + 7. What is the role of the third (addr) argument to mps_ld_isstale? + LDIsStale says "UNUSED(addr);" so maybe it is unused. + + 8. Is the material in the pool class comparison table at all accurate? + + 9. This code seems a bit confused about what to do: -4. Why does the Scheme example have a copy method in its object format - when the reference manual says it's obsolete? Can I remove it? + assert(0); + fprintf(stderr, "Unexpected object on the heap\n"); + abort(); + return MPS_RES_FAIL; + + I took out the assertion and the return. + +10. How does fixing interact with tagged references? Do I need to + remove the tag before fixing a reference? Do I need to restore the + tag afterwards? I thought that both would be necessary but the + critical path documentation has an example from OpenDylan with + tagged references that does neither: + + +11. This code from mps_chat in the Scheme example is wrong: + + if (type == mps_message_type_gc_start()) { + printf("Collection %lu started.\n", (unsigned long)mps_collections(arena)); + + mps_collections returns the total number of collections to date, + not the number of the collection that posted the message. This + means that if there have been multiple collections since the last + time the message queue was emptied, the output will look like + + Collection 47 started. + ... + Collection 47 started. + ... + Collection 47 started. + ... + Collection 47 started. + +12. It seems "tricky" to re-use fowarding objects as padding objects + by setting their forwarding pointer to NULL. It would be simpler + to explain if we had TYPE_PAD for multiple-word padding objects. + There's enough to explain as it is! + + I made this change. + +13. The Scheme example says, "Adapting it to use the MPS took + approximately two hours". I doubt this would be the common case, + and it would be better to under-promise and over-deliver. + +14. I need to document the values in mps_gen_param_s. I believe they + are the capacity (size of the generation in kilobytes) and the + mortality (the proportion of objects in this generation that are + expected to die in a collection). But what do they mean to the + MPS? And how should one go about choosing values? DONE diff --git a/mps/manual/source/reference/index.rst b/mps/manual/source/reference/index.rst index f571672707b..2e2fa4b78d7 100644 --- a/mps/manual/source/reference/index.rst +++ b/mps/manual/source/reference/index.rst @@ -1173,8 +1173,8 @@ Declared in ``mps.h`` browsers), or a null pointer if this is not possible. It is recommended that a null pointer be returned for - :term:`padding objects ` and :term:`forwarded - objects `. + :term:`padding objects ` and :term:`forwarding + objects `. The exact meaning of the return value is up to the :term:`client program`, but it would typically bear some relation to a class or @@ -1295,7 +1295,7 @@ Declared in ``mps.h`` *addr* is the address of a candidate object. - If the *addr* is the address of a :term:`forwarded object`, return + If the *addr* is the address of a :term:`forwarding object`, return the address where the object was moved to. This must be the value of the *new* argument supplied to the :term:`forward method` when the object was moved. If not, return a null pointer. @@ -1362,10 +1362,10 @@ Declared in ``mps.h`` :c:macro:`MPS_RES_OK`. The scan method for an object format is called when the MPS needs - to scan objects in a block area of memory containing objects - belonging to that format. The scan method is called with a scan - state and the base and limit of the block of objects to scan. It - must then indicate references within the objects by calling + to scan objects in a block of memory containing objects belonging + to that format. The scan method is called with a scan state and + the base and limit of the block of objects to scan. It must then + indicate references within the objects by calling :c:func:`MPS_FIX1` and :c:func:`MPS_FIX2`. .. topics::