1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-03-26 08:41:47 -07:00

Convert message, pool, poolamc, prot, and vm design documents to restructuredtext.

Copied from Perforce
 Change: 182116
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Gareth Rees 2013-05-23 17:44:52 +01:00
parent 18f29f427c
commit 955c41b0fe
12 changed files with 1732 additions and 4 deletions

427
mps/design/message.txt Normal file
View file

@ -0,0 +1,427 @@
.. mode: -*- rst -*-
Client message protocol
=======================
:Tag: design.mps.message
:Author: David Jones
:Date: 1997-02-13
:Status: incomplete document
:Revision: $Id$
:Copyright: See `Copyright and License`_.
Introduction
------------
_`.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 [bogus, since we cannot force clients to
check error codes either - Pekka 1997-09-17].
_`.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.
_`.readership`: Any MPS developer.
Requirements
------------
_`.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
------------------
_`.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
.........
_`.if.fun`: The following functions are provided:
_`.if.fun.poll`: ``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.
_`.if.fun.enable`: ``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).
_`.if.fun.disable`: ``mps_message_type_disable()`` disables the flow
of messages of a certain type. The antidote to
``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.
_`.if.fun.get`: ``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 ``TRUE``. Otherwise it will return
``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 ``mps_message_discard()``; failure to do so will result in a
resource leak.
_`.if.fun.discard`: ``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.
_`.if.fun.type.any`: ``mps_message_queue_type()`` determines the type
of a message in the queue. Returns ``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
``FALSE``.
_`.if.fun.type`: ``mps_message_type()`` determines the type of a
message (that has already been got). Only legal when inside a message
transaction (that is, after ``mps_message_get()`` and before
``mps_message_discard()``). Note that the type will be the same as the
type that the client passed in the call to ``mps_message_get()``.
Types of messages
.................
_`.type`: The type governs the "shape" and meaning of the message.
_`.type.int`: Types themselves will just be a scalar quantity, an
integer.
_`.type.semantics`: A type indicates the semantics of the message.
_`.type.semantics.interpret`: The semantics of a message are
interpreted by the client by calling various accessor methods on the
message.
_`.type.accessor`: The type of a message governs which accessor
methods are legal to apply to the message.
_`.type.example`: Some example types:
_`.type.finalization`: There will be a finalization type. The type is
abstractly: ``FinalizationMessage(Ref)``.
_`.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).
_`.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 ``mps_message_finalization_ref()``.
_`.type.finalization.ref.scan`: Note that the reference returned
should be stored in scanned memory.
Compatibility issues
....................
_`.compatibility`: The following issues affect future compatibility of
the interface:
_`.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 ``mps_message_type_enable()``, `.if.fun.enable`_).
_`.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
.....
``typedef struct MessageStruct *Message``
_`.message.type`: ``Message`` is the type of messages.
_`.message.instance`: Messages are instances of Message Classes.
``typedef struct MessageStruct *MessageStruct``
_`.message.concrete`: Concretely a message is represented by a
``MessageStruct``. A ``MessageStruct`` has the usual signature field
(see design.mps.sig). A ``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
``MessageClass`` object.
_`.message.intent`: The intention is that a ``MessageStruct`` will be
embedded in some richer object which contains information relevant to
that specific type of message.
_`.message.struct`: The structure is declared as follows::
struct MessageStruct {
Sig sig;
MessageType type;
MessageClass class;
RingStruct node;
} MessageStruct;
``typedef struct MessageClassStruct *MessageClass``
_`.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).
_`.class.concrete`: Concretely a message class is represented by a
``MessageClassStruct`` (a struct). Clients of the Message module are
expected to allocate storage for and initialise the
``MessageClassStruct``. It is expected that such storage will be
allocated and initialised statically.
_`.class.one-type`: A message class implements exactly one message
type. The identifier for this type is stored in the ``type`` field of
the ``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).
_`.class.methods.generic`: The generic methods are as follows:
* ``delete`` -- used when the message is destroyed (by the client
calling ``mps_message_discard()``). The class implementation should
finish the message (by calling ``MessageFinish()``) and storage for
the message should be reclaimed (if applicable).
_`.class.methods.specific`: The type specific methods are:
_`.class.methods.specific.finalization`: Specific to
``MessageTypeFinalization``:
* ``finalizationRef`` -- returns a reference to the finalizable object
represented by this message.
_`.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.
_`.class.sig.double`: The ``MessageClassStruct`` has a signature field
at both ends. This is so that if the ``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.
_`.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;
_`.space.queue`: The arena structure is augmented with a structure for
managing for queue of pending messages. This is a ring in the
``ArenaStruct``::
struct ArenaStruct
{
...
RingStruct messageRing;
...
}
Functions
.........
``void MessageInit(Arena arena, Message message, MessageClass class)``
_`.fun.init`: Initializes the ``MessageStruct`` pointed to by
``message``. The caller of this function is expected to manage the
store for the ``MessageStruct``.
``void MessageFinish(Message message)``
_`.fun.finish`: Finishes the ``MessageStruct`` pointed to by
``message``. The caller of this function is expected to manage the
store for the ``MessageStruct``.
``void MessagePost(Arena arena, Message message)``
_`.fun.post`: Places a message on the queue of an arena.
_`.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).
``void MessageEmpty(Arena arena)``
_`.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.
_`.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
------------------
_`.life`: A message will be allocated by a client of the message
module, it will be initialised by calling ``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
............
[possibly out of date, see design.mps.finalize and design.mps.poolmrg
instead -- drj 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 ``MessageStruct``.
The ``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.
Document History
----------------
- 1997-02-13 David Jones. incomplete document.
- 2002-06-07 RB_ Converted from MMInfo database design document.
- 2006-10-25 Richard Kistruck. Created guide.
- 2006-12-11 Richard Kistruck. More on lifecycle; unmention evil hack
in initial design.
- 2008-12-19 Richard Kistruck. Simplify and clarify lifecycle. Remove
description of and deprecate re-use of messages.
- 2013-05-23 GDR_ Converted to reStructuredText.
.. _RB: http://www.ravenbrook.com/consultants/rb/
.. _GDR: http://www.ravenbrook.com/consultants/gdr/
Copyright and License
---------------------
Copyright © 2013 Ravenbrook Limited. All rights reserved.
<http://www.ravenbrook.com/>. 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.**

115
mps/design/pool.txt Normal file
View file

@ -0,0 +1,115 @@
.. mode: -*- rst -*-
Pool and pool class mechanisms
==============================
:Tag: design.mps.pool
:Author: Richard Brooksby
:Date: 1996-07-31
:Status: incomplete document
:Revision: $Id$
:Copyright: See `Copyright and License`_.
Definitions
-----------
_`.def.outer-structure`: The "outer structure" (of a pool) is a C
object of type ``PoolXXXStruct`` or the type ``struct PoolXXXStruct``
itself.
_`.def.generic-structure`: The "generic structure" is a C object of
type ``PoolStruct`` (found embedded in the outer-structure) or the
type ``struct PoolStruct`` itself.
Defaults
--------
_`.align`: When initialised, the pool gets the default alignment
(``ARCH_ALIGN``).
_`.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.
_`.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.
_`.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
------------
[Placeholder: must derive the requirements from the architecture.]
_`.req.fix`: ``PoolFix()`` must be fast.
Other
-----
Interface in mpm.h
Types in mpmst.h
See also design.mps.poolclass
Document History
----------------
- 1996-07-31 richard incomplete doc
- 2002-06-07 RB_ Converted from MMInfo database design document.
- 2013-05-23 GDR_ Converted to reStructuredText.
.. _RB: http://www.ravenbrook.com/consultants/rb/
.. _GDR: http://www.ravenbrook.com/consultants/gdr/
Copyright and License
---------------------
Copyright © 2013 Ravenbrook Limited. All rights reserved.
<http://www.ravenbrook.com/>. 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.**

837
mps/design/poolamc.txt Normal file
View file

@ -0,0 +1,837 @@
.. mode: -*- rst -*-
AMC pool class
==============
:Tag: design.mps.poolamc
:Author: Richard Brooksby
:Date: 1995-08-25
:Status: incomplete design
:Revision: $Id$
:Copyright: See `Copyright and License`_.
Introduction
~~~~~~~~~~~~
_`.intro`: This document contains a guide (`.guide`_) to the MPS AMC
pool class, followed by the historical initial design
(`.initial-design`_).
_`.readership`: Any MPS developer.
Guide
~~~~~
_`.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
--------------
_`.seg.state`: AMC segments are in one of three states: "mobile",
"boarded", or "stuck".
_`.seg.state.mobile`: Segments are normally **mobile**: all objects on
the seg are un-nailed, and thus may be preserved by copying.
_`.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.
_`.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/
_`.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).
_`.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_.)
_`.pad.reason.bef`: Buffer empty fragment (BEF) pads are made by
``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.
_`.pad.reason.lsp`: Large segment padding (LSP) pads are made by
``AMCBufferFill()`` when the requested fill size is "large" (see `The
LSP payoff calculation`_ below). ``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, ``AMCBufferFill()`` fills any remainder with an large segment
pad.
_`.pad.reason.nmr`: Non-mobile reclaim (NMR) pads are made by
``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 ``amcReclaimNailed()`` finds no objects to be preserved then it
calls ``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 ``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 */
}</code></pre></blockquote>
``AMCLargeSegPAGES`` is currently 8 -- see `The LSP payoff
calculation`_ below.
AMC might treat "Large" segments specially, in two ways:
- _`.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.
- _`.large.lsp-no-retain`: Nails to such an LSP pad do not cause
AMCReclaimNailed() to retain the segment.
`.large.single-reserve`_ is implemented. See job001811_.
`.large.lsp-no-retain`_ is **not** currently implemented.
The point of `.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 `.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
(`.large.single-reserve`_) is only used for a single *object*, then
``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
``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 ``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
`.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
``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
------------
_`.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.
_`.readership`: The intended readership is any MPS developer.
Overview
--------
_`.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.
[ lot of this design is awesomely old -- drj 1998-02-04]
Definitions
-----------
_`.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
--------
_`.seg.class`: AMC allocates segments of class ``AMCSegClass``, which
is a subclass of ``GCSegClass``. Instances contain a ``segTypeP``
field, which is of type ``int*``.
_`.seg.gen`: AMC organizes the segments it manages into generations.
_`.seg.gen.map`: Every segment is in exactly one generation.
_`.seg.gen.ind`: The segment's ``segTypeP`` field indicates which
generation (that the segment is in) (an ``AMCGenStruct`` see blah
below).
_`.seg.typep`: The ``segTypeP`` field actually points to either the
type field of a generation or to the type field of a nail board.
_`.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.
_`.seg.gen.get`: The map from segment to generation is implemented by
``AMCSegGen()`` which deals with all this.
Fixing and nailing
------------------
_`.fix.nail`: [.fix.nail.* are placeholders for design rather than
design really -- drj 1998-02-04]
_`.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.
_`.nailboard.create`: Nail boards are allocated dynamically whenever a
segment becomes newly ambiguously referenced.
_`.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.
_`.nailboard.emergency`: During emergency tracing two things relating
to nail boards happen that don't normally:
1. _`.nailboard.emergency.nonew`: Nail boards aren't allocated when we
have new ambiguous references to segments.
_`.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. _`.nailboard.emergency.exact`: nail boards are used to record exact
references in order to avoid copying the objects.
_`.nailboard.hyper-conservative`: Not creating new nail boards
(`.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.
_`.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.
_`.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
`.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.
_`.fix.nail.distinguish`: The nailed bits in the segment descriptor
(``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 `.seg.typep`_
above).
_`.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.
_`.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.
_`.nailboard.limitations.reclaim`: ``AMCReclaimNailed()`` could cover
each block of reclaimed objects between two nailed objects with a
single padding object, speeding up further scans.
Emergency tracing
-----------------
_`.emergency.fix`: ``AMCFixEmergency()`` is at the core of AMC's
emergency tracing policy (unsurprisingly). ``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).
_`.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 ``FixEmergency()`` may introduce new marks into the nail
board.
Buffers
-------
_`.buffer.class`: AMC uses buffer of class ``AMCBufClass`` (a subclass
of SegBufClass).
_`.buffer.gen`: Each buffer allocates into exactly one generation.
_`.buffer.field.gen`: ``AMCBuf`` buffer contain a gen field which
points to the generation that the buffer allocates into.
_`.buffer.fill.gen`: ``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.
_`.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 [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 -- drj 1998-06-01 But it's probably more efficient than
keeping the buffer on the segment, because then the other stuff gets
nailed -- pekka 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
-----
_`.struct`: ``AMCStruct`` is the pool class AMC instance structure.
_`.struct.pool`: Like other pool class instances, it contains a
``PoolStruct`` containing the generic pool fields.
_`.struct.format`: The ``format`` field points to a ``Format``
structure describing the object format of objects allocated in the
pool. The field is intialized by ``AMCInit()`` from a parameter, and
thereafter it is not changed until the pool is destroyed. [actually
the format field is in the generic ``PoolStruct`` these days. drj
1998-09-21]
[lots more fields here]
Generations
-----------
_`.gen`: Generations partition the segments that a pool manages (see
`.seg.gen.map`_ above).
_`.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.
_`.gen.rep`: Generations are represented using an ``AMCGenStruct``
structure.
_`.gen.create`: All the generation are create when the pool is created
(during ``AMCInitComm()``).
_`.gen.manage.ring`: An AMC's generations are kept on a ring attached
to the ``AMCStruct`` (the ``genRing`` field).
_`.gen.manage.array`: They are also kept in an array which is
allocated when the pool is created and attached to the ``AMCStruct``
(the gens field holds the number of generations, the ``gen`` field
points to an array of ``AMCGen``). [it seems to me that we could
probably get rid of the ring -- drj 1998-09-22]
_`.gen.number`: There are ``AMCTopGen + 2`` generations in total.
"normal" generations numbered from 0 to ``AMCTopGen`` inclusive and an
extra "ramp" generation (see `.gen.ramp`_ below).
_`.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 ``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
``AMCBufferFill()`` where it checks that ``buffer->p`` is an
``AMCGen``) if you try to forward an object out of such a generation.
_`.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 ``AMCInitComm()``).
Ramps
-----
_`.ramp`: Ramps usefully implement the begin/end
``mps_alloc_pattern_ramp()`` interface.
_`.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).
_`.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``).
_`.gen.ramp.particular`: the ramp generation is the second oldest
generation and the after-ramp generation is the oldest generation.
_`.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.
_`.gen.ramp.ramping`: The ramp generation is promoted into itself
during ramping mode;
_`.gen.ramp.after`: after this mode ends, the ramp generation is
promoted into the after-ramp generation as usual.
_`.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.
_`.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.
_`.ramp.state.cycle.usual`: The usual progression of states is a
cycle: OUTSIDE → BEGIN → RAMPING → FINISH → COLLECTING → OUTSIDE.
_`.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
``AMCRampBegin()`` and ``AMCRampEnd()``).
_`.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.
_`.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.
_`.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.
_`.ramp.begin`: When the count goes up from zero, the state moves from
COLLECTING or OUTSIDE to BEGIN.
_`.ramp.begin.leave`: We can leave the BEGIN state to either the
OUTSIDE or the RAMPING state.
_`.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.
_`.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 (`.gen.ramp.ramping`_).
_`.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.
_`.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.
_`.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.
_`.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
``AMCRampBegin()``, but ignored there).
Headers
-------
_`.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.
_`.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``).
_`.header.fix`: There are two versions of the fix method, due to its
criticality, with (``AMCHeaderFix()``) and without (``AMCFix()``)
headers. The correct one is selected in ``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
------------------------------
``void AMCFinish(Pool pool)``
_`.finish.forward`: If the pool is being destroyed it is OK to destroy
the forwarding buffers, as the condemned set is about to disappear.
``void AMCBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit)``
_`.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.
_`.flush.pad`: The group is padded out with a dummy object so that it
appears full.
_`.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 `.fill.expose`_.
_`.flush.cover`: The buffer needs covering whether it was being used
for forwarding or not. See `.flush.expose`_.
``Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, Pool pool, Buffer buffer, Size size, Bool withReservoirPermit)``
_`.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.
_`.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
`.flush.cover`_.
``Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO)``
_`.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.
_`.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.
_`.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.
``Res AMCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg)``
_`.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.
``void AMCReclaim(Pool pool, Trace trace, Seg seg)``
_`.reclaim`: After a trace, destroy any groups which are still
condemned for the trace, because they must be dead.
_`.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.
_`.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.
Document History
----------------
- 1995-08-25 RB_ Incomplete design.
- 2002-06-07 RB_ Converted from MMInfo database design document.
- 2009-08-11 Richard Kistruck. Fix HTML duplicated anchor names
(caused by auto-conversion to HTML).
- 2009-08-11 Richard Kistruck. Prepend Guide, using
design/template-with-guide.html.
- 2009-09-14 Richard Kistruck. Guide covers: seg states; pads;
retained pages.
- 2013-05-23 GDR_ Converted to reStructuredText.
.. _RB: http://www.ravenbrook.com/consultants/rb/
.. _GDR: http://www.ravenbrook.com/consultants/gdr/
Copyright and License
---------------------
Copyright © 2013 Ravenbrook Limited. All rights reserved.
<http://www.ravenbrook.com/>. 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.**

141
mps/design/prot.txt Normal file
View file

@ -0,0 +1,141 @@
.. mode: -*- rst -*-
The protection module
=====================
:Tag: design.mps.prot
:Author: David Jones
:Date: 1997-04-02
:Status: incomplete document
:Revision: $Id$
:Copyright: See `Copyright and License`_.
Introduction
------------
_`.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.
_`.readership`: Any MPS developer.
Interface
---------
``void ProtSetup(void)``
_`.if.setup`: ``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)
``void ProtSet(Addr base, Addr limit, AccessSet mode)``
_`.if.set`: ``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.
``void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), void *p, size_t s)``
_`.if.tramp`: [undocumented]
``void ProtSync(Space space)``
_`.if.sync`: ``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.
``typedef struct MutatorFaultContextStruct *MutatorFaultContext``
_`.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 ``ProtCanStepInstruction()`` (`.if.canstep`_ below) and
``ProtStepInstruction()`` (`.if.step`_ below) inspect and manipulate
the context.
``Bool ProtCanStepInstruction(MutatorFaultContext context)``
_`.if.canstep`: Examines the context to determine whether the
protection module can single-step the instruction which is causing the
fault. Should return ``TRUE`` if and only if the instruction can be
single-stepped (that is, ``ProtStepInstruction()`` can be called).
``Bool Res ProtStepInstruction(MutatorFaultContext context)``
_`.if.step`: Single-steps the instruction which is causing the fault.
This function should only be called if ``ProtCanStepInstruction()``
applied to the context returned ``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.
Document History
----------------
- 1997-04-02 David Jones. Incomplete document.
- 2002-06-07 RB_ Converted from MMInfo database design document.
- 2013-05-23 GDR_ Converted to reStructuredText.
.. _RB: http://www.ravenbrook.com/consultants/rb/
.. _GDR: http://www.ravenbrook.com/consultants/gdr/
Copyright and License
---------------------
Copyright © 2013 Ravenbrook Limited. All rights reserved.
<http://www.ravenbrook.com/>. 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.**

171
mps/design/vm.txt Normal file
View file

@ -0,0 +1,171 @@
.. mode: -*- rst -*-
Virtual mapping
===============
:Tag: design.mps.vm
:Author: richard
:Date: 1998-05-11
:Status: incomplete design
:Revision: $Id$
:Copyright: See `Copyright and License`_.
Introduction
------------
_`.intro`: This the design of the VM interface. The VM interface
provides a simple, low-level, operating-system independent interface
to address-space. Each call to ``VMCreate()`` reserves (from the
operating-system) a single contiguous range of addresses, and returns
a VMStruct thereafter used to manage this address-space. The VM
interface has separate implementations for each platform that supports
it (at least conceptually, in practice some of them may be the same).
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.
_`.motivation`: The VM is used by the VM Arena Class. It provides the
basic substrate to provide sparse address maps. Sparse address maps
have at least two uses: to encode information into the address of an
object which is used in tracing (the Zone Test) to speed things up; to
avoid fragmentation at the segment level and above (since the amount
of address space reserved is large compared to the RAM, the hope is
that there will also be enough address space somewhere to fit any
particular segment in).
Definitions
-----------
_`.def.reserve`: The "reserve" operation: Exclusively reserve a
portion of the virtual address space without arranging RAM or backing
store for the virtual addresses. The intention is that no other
component in the process will make use of the reserved virtual
addresses, but in practice this may entail assuming a certain amount
of cooperation. When reserving address space, the requester simply
asks for a particular size, not a particular range of virtual
addresses. Accessing (read/write/execute) reserved addresses is
illegal unless those addresses have been mapped.
_`.def.map`: The "map" operation: Arrange that a specified portion of
the virtual address space is mapped from the swap, effectively
allocating RAM and/or swap space for a particular range of addresses.
If successful, accessing the addresses is now legal. Only reserved
addresses should be mapped.
_`.def.unmap`: The "unmap" operation: The inverse of the map
operation. Arrange that a specified portion of the virtual address
space is no longer mapped, effectively freeing up the RAM and swap
space that was in use. Accessing the addresses is now illegal. The
addresses return to the reserved state.
_`.def.vm`: "VM" stands for Virtual Memory. Various meanings: A
processor architecture's virtual space and structure; The generic idea
/ interface / implementation of the MPS VM module; The C structure
(struct VMStruct) used to encapsulate the functionality of the MPS VM
module; An instance of such a structure.
_`.def.vm.mps`: In the MPS, a "VM" is a ``VMStruct``, providing access
to the single contiguous range of address-space that was reserved
(from the operating-system) when ``VMCreate()`` was called.
Interface
---------
``Res VMCreate(VM *VMReturn, Size size)``
_`.if.create`: ``VMCreate()`` is responsible both for allocating a
``VMStruct`` and for reserving an amount of virtual address space. A
VM is created and a pointer to it is returned in the return parameter
``VMReturn``. This VM has at least size bytes of virtual memory
reserved. If there's not enough space to allocate the VM,
``ResMEMORY`` is returned. If there's not enough address space to
reserve a block of the given size, ``ResRESOURCE`` is returned. The
reserved virtual memory can be mapped and unmapped using ``VMMap()``
and ``VMUnmap()``.
``void VMDestroy(VM vm)``
_`.if.destroy`: A VM is destroyed by calling ``VMDestroy()``. Any
address space that was mapped through this VM is unmapped.
[lots of interfaces missing here]
Notes
-----
_`.testing`: It is important to test that a VM implementation will
work in extreme cases.
_`.testing.large`: It must be able to reserve a large address space.
Clients will want multi-GB spaces, more than that OSs will allow. If
they ask for too much, ``mps_arena_create()`` (and hence
``VMCreate()``) must fail in a predictable way.
_`.testing.larger`: It must be possible to allocate in a large space;
sometimes commiting will fail, because there's not enough space to
replace the "reserve" mapping. See request.epcore.160201 for details.
_`.testing.lots`: It must be possible to have lots of mappings. The OS
must either combine adjacent mappings or have lots of space in the
kernel tables. See request.epcore.160117 for ideas on how to test
this.
Document History
----------------
- 1998-05-11 RB_ Incomplete design.
- 2002-06-07 RB_ Converted from MMInfo database design document.
- 2013-05-23 GDR_ Converted to reStructuredText.
.. _RB: http://www.ravenbrook.com/consultants/rb/
.. _GDR: http://www.ravenbrook.com/consultants/gdr/
Copyright and License
---------------------
Copyright © 2013 Ravenbrook Limited. All rights reserved.
<http://www.ravenbrook.com/>. 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.**

View file

@ -0,0 +1,7 @@
.. index::
pair: messages; design
single: client message protocol
.. _design-message:
.. include:: ../../converted/message.rst

View file

@ -23,8 +23,12 @@ Old design
finalize
fix
lock
message
object-debug
pool
poolamc
poolmvff
prot
protocol
reservoir
root
@ -37,4 +41,5 @@ Old design
type
version-library
version
vm
writef

View file

@ -0,0 +1,6 @@
.. index::
pair: pool class mechanism; design
.. _design-pool:
.. include:: ../../converted/pool.rst

View file

@ -0,0 +1,7 @@
.. index::
pair: AMC pool; design
single: pool; AMC design
.. _design-poolamc:
.. include:: ../../converted/poolamc.rst

View file

@ -0,0 +1,6 @@
.. index::
pair: protection interface; design
.. _design-prot:
.. include:: ../../converted/prot.rst

View file

@ -0,0 +1,6 @@
.. index::
pair: virtual mapping; design
.. _design-vm:
.. include:: ../../converted/vm.rst

View file

@ -8,10 +8,10 @@ from sys import stdout
TYPES = '''
AccessSet Accumulation Addr Align AP Arg Arena Attr Bool BT Buffer
Byte Clock Compare Count Epoch Format Fun Index LD Lock Pointer
Pool Rank RankSet Ref Res Reservoir Ring Root RootVar ScanState
Seg Serial Shift Sig Size Space SplayNode SplayTree Thread Trace
TraceId TraceSet ULongest VM Word
Byte Clock Compare Count Epoch Format Fun Index LD Lock Message
Pointer Pool Rank RankSet Ref Res Reservoir Ring Root RootVar
ScanState Seg Serial Shift Sig Size Space SplayNode SplayTree
Thread Trace TraceId TraceSet ULongest VM Word
'''