1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-05-01 10:41:57 -07:00

Convert alloc-frame, diag, interface-c, and io design documents to restructuredtext. and that's the lot! (cbs and poolmvt are already converted on mps/branch/2013-05-17/emergency).

Copied from Perforce
 Change: 182275
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Gareth Rees 2013-05-28 11:38:46 +01:00
parent b3ad8d80b3
commit 273bfaa70b
16 changed files with 2513 additions and 17 deletions

513
mps/design/alloc-frame.txt Normal file
View file

@ -0,0 +1,513 @@
.. mode: -*- rst -*-
Allocation frame protocol
=========================
:Tag: design.mps.alloc-frame
:Author: Tony Mann
:Date: 1998-10-02
:Status: incomplete document
:Revision: $Id$
:Copyright: See `Copyright and License`_.
Introduction
------------
_`.intro`: This document explains the design of the support for
allocation frames in MPS.
_`.readership`: This document is intended for any MM developer.
_`.overview`: Allocation frames are used for implementing stack pools;
each stack frame corresponds to an allocation frame. Allocation frames
may also be suitable for implementing other sub-pool groupings, such
as generations and ramp allocation patterns.
_`.overview.ambition`: We now believe this to be a design that loses
too many advantages of stack allocation for questionable gains. The
requirements are almost entirely based on unanalysed anecdote, instead
of actual clients.
.. note::
We plan to supersede this with a stack pool design at some point
in the future. Pekka P. Pirinen, 2000-03-09.
Definitions
-----------
_`.def.alloc-frame`: An allocation frame is a generic name for a
device which groups objects together with other objects at allocation
time, and which may have a parent/child relationship with other
allocation frames.
Purpose
-------
_`.purpose.stack-allocation`: The allocation frame protocol is
intended to support efficient memory management for stack allocation,
that is, the allocation of objects which have dynamic extent.
_`.purpose.general`: The allocation frame protocol is intended to be
sufficiently general that it will be useful in supporting other types
of nested allocation patterns too. For example, it could be used to
for EPVM-style save and restore, ramp allocation patterns or
generations.
Requirements
------------
Known requirements
..................
_`.req.stack-alloc`: Provide a interface for clients to describe a
stack allocation pattern, as an alternative to using the control
stack.
_`.req.efficient`: Permit an implementation which is comparable in
efficiency to allocating on the control stack.
_`.req.ap`: Support allocation via allocation points (APs).
_`.req.format`: Support the allocation of formatted objects.
_`.req.scan`: Ensure that objects in allocation frames can participate
in garbage collection by being scanned.
_`.req.fix`: Ensure that objects in allocation frames can participate
in garbage collection by accepting Fix requests.
_`.req.condemn`: Ensure that objects in allocation frames can
participate in garbage collection by being condemned.
_`.attr.locking`: Minimize the synchronization cost for the creation
and destruction of frames.
Proto-requirements
..................
_`.proto-req`: The following are possible requirements that might be
important in the future. The design does not necessarily meet all
these requirements, but it does consider them all. Each requirement
either has direct support in the framework, or could be supported with
future additions to the framework.
_`.req.parallels`: The allocation frame protocol should provide a
framework for exploiting the parallels between stack extents,
generations and "ramps".
_`.req.pool-destroy`: It should be possible to use allocation frames
to free all objects in a pool without destroying the pool.
_`.req.epvm`: It should be possible to implement EPVM-style save and
restore operations by creating and destroying allocation frames.
_`.req.subst`: It should be possible to substitute a stack pool with a
GC-ed pool so that erroneous use of a stack pool can be detected.
_`.req.format-extensions`: It should be possible for stack pools to
utilize the same format as any other pool, including debugging formats
that include fenceposting, etc.
_`.req.mis-nest`: Should ensure "mis-nested" stacks are safe.
_`.req.non-top-level`: Should support allocation in the non-top stack
extent.
_`.req.copy-if-necessary`: Should ensure that stack pools can support
"copy-if-necessary" (so that low-level system code can heapify stack
objects.)
_`.req.preserve`: When an object is in an allocation frame which is
being destroyed, it should be possible to preserve that object in the
parent frame.
_`.req.contained`: Should allow clients to ask if an object is
"contained" in a frame. The object is contained in a frame if it is
affected when the frame is ended.
_`.req.alloc-with-other`: Should allow clients to allocate an object
in the same frame as another object.
Overview
--------
_`.frame-classes`: The protocol supports different types of allocation
frames, which are represented as "frame classes". It's up to pools to
determine which classes of allocation frames they support. Pools which
support more than one frame class rely on the client to indicate which
class is currently of interest. The client indicates this by means of
an operation which stores the class in the buffer to which the
allocation point is attached.
_`.frame-handles`: Allocation frames are described via abstract "frame
handles". Pools may choose what the representation of a frame handle
should be. Frame handles are static, and the client need not store
them in a GC root.
_`.lightweight-frames`: The design includes an extension to the
allocation point protocol, which permits the creation and destruction
of allocation frames without the necessity for claiming the arena
lock. Such frames are called "lightweight frames".
Operations
----------
_`.op.intro`: Each operation has both an external (client) interface
and an internal (MPS) interface. The external function takes an
allocation point as a parameter, determines which buffer and pool it
belongs to, and calls the internal function with the buffer and pool
as parameters.
_`.op.obligatory`: The following operations are supported on any
allocation point which supports allocation frames:-
_`.operation.push`: The ``PushFrame()`` operation creates a new
allocation frame of the currently chosen frame class, makes this new
frame the current frame, and returns a handle for the frame.
_`.operation.pop`: The ``PopFrame()`` operation takes a frame handle
as a parameter. Some pool classes might insist or assume that this is
the handle for the current frame. It finds the parent of that frame
and makes it the current frame. The operation indicates that all
children of the new current frame contain objects which are likely to
be dead. The reclaim policy is up to the pool; some classes might
insist or assume that the objects must be dead, and eagerly free them.
Note that this might introduce the possibility of leaving dangling
pointers elsewhere in the arena. If so, it's up to the pool to decide
what to do about this.
_`.op.optional`: The following operations are supported for some
allocation frames, but not all. Pools may choose to support some or
all of these operations for certain frame classes. An unsupported
operation will return a failure value:-
_`.operation.select`: The ``SelectFrame()`` operation takes a frame
handle as a parameter and makes that frame the current frame. It does
not indicate that any children of the current frame contain objects
which are likely to be dead.
_`.operation.select-addr`: The ``SelectFrameOfAddr()`` operation takes
an address as a parameter and makes the frame of that address the
current frame. It does not indicate that any children of the current
frame contain objects which are likely to be dead.
_`.operation.in-frame`: The ``AddrInFrame()`` operation determines
whether the supplied address is the address of an object allocated in
the supplied frame, or any child of that frame.
_`.operation.set`: The ``SetFrameClass()`` operation takes a frame
class and an allocation point as parameters, and makes that the
current frame class for the allocation point. The next ``PushFrame()``
operation will create a new frame of that class.
Interface
---------
External types
..............
_`.type.client.frame-handle`: Frame handles are defined as the abstract
type ``mps_frame_t``.
``typedef struct mps_frame_class_s *mps_frame_class_t``
_`.type.client.frame-class`: Frame classes are defined as an abstract
type.
_`.type.client.frame-class.access`: Clients access frame classes by
means of dedicated functions for each frame class.
External functions
..................
_`.fn.client.push`: ``mps_ap_frame_push()`` is used by clients to
invoke the ``PushFrame()`` operation. For lightweight frames, this
might not invoke the corresponding internal function.
_`.fn.client.pop`: ``mps_ap_frame_pop()`` is used by clients to invoke
the ``PopFrame()`` operation. For lightweight frames, this might not
invoke the corresponding internal function.
``mps_res_t mps_ap_frame_select(mps_ap_t buf, mps_frame_t frame)``
_`.fn.client.select`: This following function is used by clients to
invoke the ``SelectFrame()`` operation.
``mps_res_t mps_ap_frame_select_from_addr(mps_ap_t buf, mps_addr_t addr)``
_`.fn.client.select-addr`: This function is used by clients to invoke
the ``SelectFrameOfAddr()`` operation.
``mps_res_t mps_ap_addr_in_frame(mps_bool_t *inframe_o, mps_ap_t buf, mps_addr_t *addrref, mps_frame_t frame)``
_`.fn.client.in-frame`: This function is used by clients to invoke the
``AddrInFrame()`` operation.
``mps_res_t mps_ap_set_frame_class(mps_ap_t buf, mps_frame_class_t
class)``
_`.fn.client.set`: This function is used by clients to invoke the
``SetFrameClass()`` operation.
``mps_frame_class_t mps_alloc_frame_class_stack(void)``
_`.fn.client.stack-frame-class`: This function is used by clients to
access the frame class used for simple stack allocation.
Internal types
..............
``typedef struct AllocFrameStruct *AllocFrame``
_`.type.frame-handle`: Frame handles are defined as an abstract type.
``typedef struct AllocFrameClassStruct *AllocFrameClass``
_`.type.frame-class`: Frame classes are defined as an abstract type.
``typedef Res (*PoolFramePushMethod)(AllocFrame *frameReturn, Pool pool, Buffer buf)``
_`.fn.push`: A pool method of this type is called (if needed) to
invoke the ``PushFrame()`` operation.
``typedef Res (*PoolFramePopMethod)(Pool pool, Buffer buf, AllocFrame frame)``
_`.fn.pop`: A pool method of this type is called (if needed)
to invoke the PopFrame operation:
``typedef Res (*PoolFrameSelectMethod)(Pool pool, Buffer buf, AllocFrame frame)``
_`.fn.select`: A pool method of this type is called to invoke the
``SelectFrame()`` operation.
``typedef Res (*PoolFrameSelectFromAddrMethod)(Pool pool, Buffer buf, Addr addr)``
_`.fn.select-addr`: A pool method of this type is called to invoke the
``SelectFrameOfAddr()`` operation.
``typedef Res (*PoolAddrInFrameMethod)(Bool *inframeReturn, Pool pool, Seg seg, Addr *addrref, AllocFrame frame)``
_`.fn.in-frame`: A pool method of this type is called to invoke the
``AddrInFrame()`` operation.
``typedef Res (*PoolSetFrameClassMethod)(Pool pool, Buffer buf, AllocFrameClass class)``
_`.fn.set`: A pool method of this type is called to invoke the
``SetFrameClass()`` operation.
Lightweight frames
-------------------
Overview
........
_`.lw-frame.overview`: Allocation points provide direct support for
lightweight frames, and are designed to permit PushFrame and PopFrame
operations without the need for locking and delegation to the pool
method. Pools can disable this mechanism for any allocation point, so
that the pool method is always called. The pool method will be called
whenever synchronization is required for other reasons (e.g. the
buffer is tripped).
_`.lw-frame.model`: Lightweight frames offer direct support for a
particular model of allocation frame use, whereby the PushFrame
operation returns the current allocation pointer as a frame handle,
and the PopFrame operation causes the allocation pointer to be reset
to the address of the frame handle. This model should be suitable for
simple stack frames, where more advanced operations like SelectFrame
are not supported. It may also be suitable for more advanced
allocation frame models when they are being used simply. The use of a
complex operation always involves synchronization via locking, and the
pool may disable lightweight synchronization temporarily at this time.
State
.....
_`.lw-frame.states`: Allocation points supporting lightweight frames
will be in one of the following states:
============ ================================================================
Valid Indicates that ``PushFrame()`` can be a lightweight
operation and need not be synchronized.
PopPending Indicates that there has been a ``PopFrame()`` operation
that the pool must respond to.
Disabled Indicates that the pool has disabled support for lightweight
operations for this AP.
============ ================================================================
These states are in addition to the state normally held by an AP for
allocation purposes. An AP will be in the Disabled state at creation.
_`.lw-frame.transitions`: State transitions happen under the following
circumstances:
======================= ====================================================
Valid → PopPending As a result of a client ``PopFrame()``
operation.
Valid → Disabled At the choice of the pool (for example, when
responding to a ``SelectFrame()`` operation).
PopPending → Valid At the choice of the pool, when processing a
``PopFrame()``.
PopPending → Disabled At the choice of the pool, when processing a
``PopFrame()``.
Disabled → Valid At the choice of the pool.
Disabled → Popframe Illegal.
======================= ====================================================
_`.lw-frame.state-impl`: Each AP contains 3 additional fields to hold this state::
mps_addr_t frameptr;
mps_bool_t enabled;
mps_bool_t lwPopPending;
_`.lw-frame.enabled`: The ``enabled`` slot holds the following values for
each state:
========== ==========
Valid ``TRUE``
PopPending ``TRUE``
Disabled ``FALSE``
========== ==========
_`.lw-frame.frameptr`: The ``frameptr`` slot holds the following values
for each state:
========== ============================================
Valid ``NULL``
PopPending Frame handle for most recently popped frame.
Disabled ``NULL``
========== ============================================
_`.lw-frame.lwPopPending`: The ``lwPopPending`` slot holds the
following values for each state:
========== =========
Valid ``FALSE``
PopPending ``TRUE``
Disabled ``FALSE``
========== =========
_`.lw-frame.state-for-gc`: It is not necessary for the tracer, format
code, pool, or any other part of the GC support in MPS to read either
of the two additional AP fields in order to scan a segment which
supports a lightweight allocation frame.
Synchronization
...............
_`.lw-frame.sync`: The purpose of the design is that mutator may
access the state of an AP without locking with MPS (via the external
functions). The design assumes the normal MPS restriction that an
operation on an AP may only be performed by a single mutator thread at
a time. Each of the operations on allocation frames counts as an
operation on an AP.
_`.lw-frame.sync.pool`: Pools are permitted to read or modify the
lightweight frame state of an AP only in response to an operation on
that AP.
_`.lw-frame.sync.external`: The external functions
``mps_ap_frame_push()`` and ``mps_ap_frame_pop()`` are permitted to
read the values of the ``enabled`` and ``frameptr`` fields for the
supplied AP without claiming the arena lock. They are permitted to
modify the ``frameptr`` field if and only if ``enabled == FALSE``.
_`.lw-frame.sync.trip`: When a buffer trip happens, and the trap
wasn't set by MPS itself (that is, it wasn't because of a flip or for
logging), then the buffer code must check whether the AP has state
PopPending. If it does, the buffer code must call the Pool.
Implementation
..............
_`.lw-frame.push`: The external ``PushFrame()`` operation
(``mps_ap_frame_push()``) performs the following operations::
IF (!APIsTrapped(ap) && StateOfFrame(ap) == Valid && ap->init == ap->alloc)
*frame_o = ap->init;
ELSE
WITH_ARENA_LOCK
PerformInternalPushFrameOperation(...)
END
END
_`.lw-frame.pop`: The external ``PopFrame()`` operation
(``mps_ap_frame_pop()``) performs the following operations::
IF (StateOfFrame(ap) != Disabled)
TrapAP(ap); /* ensure next allocation or push involves the pool */
ap->frameptr = frame;
ap->lwpopPending = TRUE;
ELSE
WITH_ARENA_LOCK
PerformInternalPopFrameOperation(...)
END
END
Document History
----------------
- 1998-10-02 Tony Mann. 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.**

355
mps/design/diag.txt Normal file
View file

@ -0,0 +1,355 @@
.. mode: -*- rst -*-
Diagnostic feedback
===================
:Tag: design.mps.diag
:Author: Richard Kistruck
:Date: 2007-06-28
:Status: incomplete design
:Revision: $Id$
:Copyright: See `Copyright and License`_.
Introduction
------------
_`.intro`: This document describes how to use the diagnostic feedback
mechanism in the Memory Pool System.
_`.sources`: Initially abased on `[RHSK 2007-04-13]`_ and `[RHSK
2007-04-18]`_.
Overview
--------
Diagnostic feedback is information created by the MPS diagnostic
system for the purpose of helping MPS programmers client-code
programmers.
Such a piece of information is called "a diagnostic". (See also
`.parts`_.)
A diagnostic is not intended to be end-user readable (or visible), or
machine-parseable.
A diagnostic is not intended to be stable from one release to the
next: it may be modified or removed at any time.
Requirements
------------
MPS diagnostic feedback code must do these things:
- calculate, store, and propagate data;
- collate, synthesise, and format it into a human-useful diagnostic;
- control (for example, filter) output of diagnostics;
- use a channel to get the diagnostic out.
Note: the knowledge/code/logic for constructing the human-useful
message is kept inside normal MPS source code. This means it is always
in-sync with changes to the MPS. This also means that any external
utilities used to display the messages do not need to understand, or
keep in sync with, the details of what's going inside the MPS.
Usage
-----
To run the MPS and get diagnostic output from it:
1. Use a variety with diagnostics compiled-in. Currently, that means
variety.di. See ``config.h``.
2. Check that the diagnostics you require are generated, by looking in
MPS source for invocations of the appropriate macro (for example,
``DIAG_SINGLEF()``).
3. Check that the diagnostics you require will be output, by looking
at the diagnostic filter rules in ``diag.c``.
4. Run the MPS and client in an environment that supports the channel
used (for example, at a command-line if using ``WriteF()``).
What is a diagnostic?
.....................
A diagnostic has three parts:
1. a trigger condition, that causes this diagnostic to be emitted;
2. a text tag (for example, "TraceStart") which is the name of this
diagnostic; and
3. a paragraph of human-useful text.
A diagnostic is emitted by the MPS at a certain point in time when a
certain event happens.
Diagnostics are not nested. Every diagnostic must have a tag. Each
diagnostic should have a unique tag (uniqueness is just to help the
humans; the diagnostic system does not care).
The paragraph of text can be many lines long. It usually explains what
event caused the diagnostic to be emitted, and commonly also includes
the output of some ``Describe()`` methods for various relevant
objects. (For example, the ``TraceStart`` diagnostic might call, and
include the output generated by, the ``TraceDescribe()`` method).
How do I control (filter) which diagnostics I see?
..................................................
All diagnostics are emitted and then filtered according to the
"diagnostic filter rules".
The first level of control is filtering by tag. (For example, only
show ``TraceStart`` diagnostics).
The second level of control is filtering by paragraph content. (For
example, only show ``TraceStart`` diagnostics where the trace is
started because a nursery generation is full).
The third level of control is filtering by line content. (For example,
only show lines containing the word ``whiteSet``).
See ``diag.c`` for details.
Note: the entire filtering mechanism can be turned off, so that
diagnostics go immediately to ``mps_lib_get_stdout(0``, with no
buffering or filtering See impl.c.diag.filter-disable.
How to write a diagnostic
-------------------------
Improve stateless Describe methods where possible
.................................................
Where possible, don't put clever code into an event-triggered
diagnostic: put it into a stateless ``Describe()`` method instead, and
then call that method when emitting your diagnostic.
For example::
FooDescribe(Foo foo, mps_lib_FILE *stream)
{
/* show value of new "quux" field */
WriteF(stream, "Foo: $P { quux: $U }\n", foo, foo->quux);
}
FooWibble(Foo foo)
{
...
DIAG_FIRSTF(( "FooWibble", "Wibbling foo $P", foo, NULL));
DIAG( FooDescribe(foo, DIAG_STREAM); );
DIAG_END("FooWibble");
...
}
This is much better, because other people can use your human-useful
output in their diagnostics, or 'live' in a debugger.
Use the output macros
.....................
For a simple diagnostic, use ``DIAG_SINGLEF()``. This begins the tag,
puts text into the paragraph, and ends the tag immediately.
For a more complex diagnostic, the first call must be
``DIAG_FIRSTF()``, which begins a diag tag.
While a tag is current, you can add text to the diagnostic's paragraph
using ``DIAG_MOREF()``, and ``WriteF( DIAG_STREAM, ... )``.
.. note::
``DIAG_STREAM`` is not a real standard C library stream. If you
want stream-level access, you may use ``Stream_fputc()`` and
``Stream_fputs()``.
End the tag by calling ``DIAG_END``.
Compile away in non-diag varieties; no side effects
...................................................
Wrap non-output code with the ``DIAG()`` and ``DIAG_DECL()`` macros,
to make sure that non-diag varieties do not execute
diagnostic-generating code.
For complex diagnostic-generating code, it may be cleaner to move it
into a separate local function. Put ``_diag`` on the end of the function
name (for example, ``TraceStart_diag()``).
Obviously, diagnostic-generating code must have no side effects.
Choosing tags
.............
Tags should be valid C identifiers. Unless you know of a good reason
why not. (Not currently checked).
There's no formal scheme for tag naming, but make it helpful and
informally hierarchical, for example, ``TraceBegin``, ``TraceStart``,
``TraceEnd``, and so on, not ``BeginTrace``, ``EndTrace``.
Writing good paragraph text
...........................
IMPORTANT: Make your diagnostics easy to understand! Other people will
read your diagnostics! Make them clear and helpful. Do not make them
terse and cryptic. If you use symbols, print a key in the diagnostic.
(If you don't want to see this the screen clutter, then you can always
add a filter rule to your personal rule set to filter it out).
Maintaining helpful filter rules
................................
If you add a noisy diagnostic, add a rule to the default ruleset to
turn it off.
How the MPS diagnostic system works
-----------------------------------
Channels
........
The recommended channel is ``WriteF()`` to standard output.
Other possible of future channels might be:
- ``printf()``;
- a new type (yet to be defined) of ``mps_message``;
- squirt them into the telemetry-log-events system;
- telnet.
Currently, only ``printf()`` and ``WriteF()`` are supported. See the
``DIAG_WITH_`` macros in ``mpm.h``.
You can also use a debugger to call ``Describe()`` methods directly,
from within the debugger.
Note: it is unfortunate that choice of channel may (for some channels)
also dictate the form of the code that synthesises the message. (For
example, ``WriteF()`` style parameter-expansion is not possible when
using the ``printf()`` channel, because there is no way to get
``WriteF()`` to produce its output into a string). This is just a
technical hitch; logically, the code that synthesises a diagnostic
message should not care which channel will be used to transmit it out
of the MPS.
Parts of the MPS diagnostic system
..................................
_`.parts`: The following facilities are considered part of the MPS
diagnostic system:
- the ``Describe()`` methods.
- the ``DIAG`` macros (``DIAG``, ``DIAG_DECL``, ``DIAG_*F``, and so on);
- the ``STATISTIC`` macros (see ``mpm.h``);
- the ``METER`` macros and meter subsystem.
Related systems
...............
The MPS diagnostic system is separate from the following other MPS
systems:
- The telemetry-log-events system. This emits much more data, in a
less human-readable form, requires MPS-aware external tools, and is
more stable from release to release). In non-diagnostic telemetry
varieties, the telemetry-log-events system emits events that log all
normal MPS actions. In diagnostic telemetry varieties, it may emit
additional events containing diagnostic information. Additionally,
the telemetry-log-events stream might in future be available as a
channel for emitting human-readable text diagnostics. See also
design.mps.telemetry.
- The MPS message system. This is present in all varieties, and
manages asynchronous communication from the MPS to the client
program). However, the MPS message system might in future also be
available as a channel for emitting diagnostics. See also
design.mps.message.
References
----------
.. _[RHSK 2007-04-13]:
Richard Kistruck. 2007-04-13. "`diagnostic feedback from the MPS <https://info.ravenbrook.com/mail/2007/04/13/13-07-45/0.txt>`_".
.. _[RHSK 2007-04-18]:
Richard Kistruck. 2007-04-18. "`Diverse types of diagnostic feedback <http://info.ravenbrook.com/mail/2007/04/18/10-58-49/0.txt>`_".
Document History
----------------
- 2007-06-28 Richard Kistruck Create. Telemetry-log-events system is a possible channel.
- 2007-06-29 Richard Kistruck Feedback (not output), each "a
diagnostic". Parts of the system; related systems. Link to initial
design email.
- 2007-08-14 Richard Kistruck (Diag Filtering). Expand section: How to
see some MPS diagnostic output, with what a diagnostic is, and how
to filter it. New section: How to write a diagnostic. Various minor
updates and corrections.
- 2013-05-23 GDR_ Converted to reStructuredText.
.. _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

@ -1,4 +1,6 @@
C Style -- Formatting
.. mode: -*- rst -*-
C Style -- formatting
=====================
:Tag: guide.impl.c.format

389
mps/design/interface-c.txt Normal file
View file

@ -0,0 +1,389 @@
.. mode: -*- rst -*-
C interface design
==================
:Tag: design.mps.interface.c
:Author: Richard Brooksby
:Date: 1996-07-29
:Status: incomplete document
:Revision: $Id$
:Copyright: See `Copyright and License`_.
Introduction
------------
_`.scope`: This document is the design for the Memory Pool System
(MPS) interface to the C Language, impl.h.mps.
_`.bg`: See mail.richard.1996-07-24.10-57.
Analysis
--------
Goals
.....
_`.goal.c`: The file impl.h.mps is the C external interface to the
MPS. It is the default interface between client code written in C and
the MPS. _`.goal.cpp`: impl.h.mps is not specifically designed to be
an interface to C++, but should be usable from C++.
Requirements
............
_`.req`: The interface must provide an interface from client code
written in C to the functionality of the MPS required by the product
(see req.product), Dylan (req.dylan), and the Core RIP (req.epcore).
``mps.h`` may not include internal MPS header files (such as
``pool.h``).
It is essential that the interface cope well with change, in order to
avoid restricting possible future MPS developments. This means that
the interface must be "open ended" in its definitions. This accounts
for some of the apparently tortuous methods of doing things
(``mps_fmt_A_t``, for example). The requirement is that the MPS should
be able to add new functionality, or alter the implementation of
existing functionality, without affecting existing client code. A
stronger requirement is that the MPS should be able to change without
*recompiling* client code. This is not always possible.
.. note::
`.naming.global` was presumably done in response to unwritten
requirements regarding the use of the name spaces in C, perhaps
these:
- _`.req.name.iso`: The interface shall not conflict in terms of
naming with any interfaces specified by ISO C and all reasonable
future versions.
- _`.req.name.general`: The interface shall use a documented and
reasonably small portion of the namespace so that clients can
interoperate easily.
David Jones, 1998-10-01.
Architecture
------------
_`.fig.arch`: The architecture of the MPS Interface
[missing figure]
Just behind ``mps.h`` is the file ``mpsi.c``, the "MPS interface
layer" which does the job of converting types and checking parameters
before calling through to the MPS proper, using internal MPS methods.
General conventions
-------------------
_`.naming`: The external interface names should adhere to the
documented interface conventions; these are found in
doc.mps.ref-man.if-conv(0).naming. They are paraphrased/recreated
here.
_`.naming.unixy`: The external interface does not follow the same
naming conventions as the internal code. The interface is designed to
resemble a more conventional C, Unix, or Posix naming convention.
_`.naming.case`: Identifiers are in lower case, except
non-function-like macros, which are in upper case.
_`.naming.global`: All publicised identifiers are
prefixed ``mps_`` or ``MPS_``.
_`.naming.all`: All identifiers defined by the MPS
should begin ``mps_`` or ``MPS_`` or ``_mps_``.
_`.naming.type`: Types are suffixed ``_t``.
_`.naming.struct`: Structure types and tags are suffixed ``_s``.
_`.naming.union`: Unions types and tags are suffixed ``_u``.
_`.naming.scope`: The naming conventions apply to all identifiers (see
ISO C §6.1.2); this includes names of functions, variables, types
(through typedef), structure and union tags, enumeration members,
structure and union members, macros, macro parameters, labels.
_`.naming.scope.labels`: labels (for ``goto`` statements) should be
rare, only in special block macros and probably not even then.
.. note::
This principle is not adhered to in the source code, which uses
``goto`` for handling error cases. Gareth Rees, 2013-05-27.
_`.naming.scope.other`: The naming convention would also extend to
enumeration types and parameters in functions prototypes but both of
those are prohibited from having names in an interface file.
_`.type.gen`: The interface defines memory addresses as ``void *`` and
sizes as ``size_t`` for compatibility with standard C (in particular,
with ``malloc()``). These types must be binary compatible with the
internal types ``Addr`` and ``Size`` respectively. Note that this
restricts the definitions of the internal types ``Addr`` and ``Size``
when the MPS is interfaced with C, but does not restrict the MPS in
general.
_`.type.opaque`: Opaque types are defined as pointers to structures
which are never defined. These types are cast to the corresponding
internal types in ``mpsi.c``.
_`.type.trans`: Some transparent structures are defined. The client is
expected to read these, or poke about in them, under restrictions
which should be documented. The most important is probably the
allocation point (``mps_ap_s``) which is part of allocation buffers.
The transparent structures must be binary compatible with
corresponding internal structures. For example, the fields of
``mps_ap_s`` must correspond with ``APStruct`` internally. This is
checked by ``mpsi.c`` in ``mps_check()``.
_`.type.pseudo`: Some pseudo-opaque structures are defined. These only
exist so that code can be inlined using macros. The client code
shouldn't mess with them. The most important case of this is the scan
state (``mps_ss_s``) which is accessed by the in-line scanning macros,
``MPS_SCAN_*`` and ``MPS_FIX*``.
_`.type.enum`: There should be no enumeration types in the interface.
Note that enum specifiers (to declare integer constants) are fine as
long as no type is declared. See guide.impl.c.misc.enum.type.
_`.type.fun`: Whenever function types or derived function types (such
as pointer to function) are declared a prototype should be used and
the parameters to the function should not be named. This includes the
case where you are declaring the prototype for an interface function.
_`.type.fun.example`: So use::
extern mps_res_t mps_alloc(mps_addr_t *, mps_pool_t, size_t, ...);
rather than::
extern mps_res_t mps_alloc(mps_addr_t *addr_return, mps_pool_t pool , size_t size, ...);
and::
typedef mps_addr_t (*mps_fmt_class_t)(mps_addr_t);
rather than::
typedef mps_addr_t (*mps_fmt_class_t)(mps_addr_t object);
See guide.impl.c.misc.prototype.parameters.
Checking
--------
_`.check.space`: When the arena needs to be recovered from a parameter
it is check using ``AVERT(Foo, foo)`` before any attempt to call
``FooArena(foo)``. The macro ``AVERT()`` in impl.h.assert performs
simple thread-safe checking of ``foo``, so it can be called outside of
``ArenaEnter()`` and ``ArenaLeave()``.
_`.check.types`: We use definitions of types in both our external
interface and our internal code, and we want to make sure that they
are compatible. (The external interface changes less often and hides
more information.) At first, we were just checking their sizes, which
wasn't very good, but I've come up with some macros which check the
assignment compatibility of the types too. This is a sufficiently
useful trick that I thought I'd send it round. It may be useful in
other places where types and structures need to be checked for
compatibility at compile time.
These macros don't generate warnings on the compilers I've tried.
``COMPATLVALUE(lvalue1, lvalue2)``
This macro checks the assignment compatibility of two lvalues. It uses
``sizeof()`` to ensure that the assignments have no effect. ::
#define COMPATLVALUE(lv1, lv2) \
((void)sizeof((lv1) = (lv2)), (void)sizeof((lv2) = (lv1)), TRUE)
``COMPATTYPE(type1, type2)``
This macro checks that two types are assignment-compatible and equal
in size. The hack here is that it generates an lvalue for each type by
casting zero to a pointer to the type. The use of ``sizeof()`` avoids
the undefined behaviour that would otherwise result from dereferencing
a null pointer. ::
#define COMPATTYPE(t1, t2) \
(sizeof(t1) == sizeof(t2) && \
COMPATLVALUE(*((t1 *)0), *((t2 *)0)))
``COMPATFIELDAPPROX(structure1, field1, structure2, field2)``
This macro checks that the offset and size of two fields in two
structure types are the same. ::
#define COMPATFIELDAPPROX(s1, f1, s2, f2) \
(sizeof(((s1 *)0)->f1) == sizeof(((s2 *)0)->f2) && \
offsetof(s1, f1) == offsetof(s2, f2))
``COMPATFIELD(structure1, field1, structure2, field2)``
This macro checks the offset, size, and assignment-compatibility of
two fields in two structure types. ::
#define COMPATFIELD(s1, f1, s2, f2) \
(COMPATFIELDAPPROX(s1, f1, s2, f2) && \
COMPATLVALUE(((s1 *)0)->f1, ((s2 *)0)->f2))
Binary compatibility issues
---------------------------
As in, "Enumeration types are not allowed" (see
mail.richard.1995-09-08.09-28).
There are two main aspects to run-time compatibility: binary interface
and protocol. The binary interface is all the information needed to
correctly use the library, and includes external symbol linkage,
calling conventions, type representation compatibility, structure
layouts, etc. The protocol is how the library is actually used by the
client code -- whether this is called before that -- and determines
the semantic correctness of the client with respect to the library.
The binary interface is determined completely by the header file and
the target. The header file specifies the external names and the
types, and the target platform specifies calling conventions and type
representation. There is therefore a many-to-one mapping between the
header file version and the binary interface.
The protocol is determined by the implementation of the library.
Constraints
-----------
_`.cons`: The MPS C Interface constrains the MPS in order to provide
useful memory management services to a C or C++ program.
_`.cons.addr`: The interface constrains the MPS address type, Addr
(design.mps.type.addr), to being the same as C's generic pointer type,
``void *``, so that the MPS can manage C objects in the natural way.
_`.pun.addr`: We pun the type of ``mps_addr_t`` (which is ``void *``)
into ``Addr`` (an incomplete type, see design.mps.type.addr). This
happens in the call to the scan state's fix function, for example.
_`.cons.size`: The interface constrains the MPS size type, ``Size``
(design.mps.type.size), to being the same as C's size type,
``size_t``, so that the MPS can manage C objects in the natural way.
_`.pun.size`: We pun the type of ``size_t`` in mps.h into ``Size`` in
the MPM, as an argument to the format methods. We assume this works.
_`.cons.word`: The MPS assumes that ``Word`` (design.mps.type.word)
and ``Addr`` (design.mps.type.addr) are the same size, and the
interface constrains ``Word`` to being the same size as C's generic
pointer type, ``void *``.
Notes
-----
The file ``mpstd.h`` is the MPS target detection header. It decodes
preprocessor symbols which are predefined by build environments in
order to determine the target platform, and then defines uniform
symbols, such as ``MPS_ARCH_I3``, for use internally by the MPS.
There is a design document for the mps interface,
design.mps.interface, but it was written before we had the idea of
having a C interface layer. It is quite relevant, though, and could be
updated. We should use it during the review.
All exported identifiers and file names should begin with ``mps_`` or
``MPS_`` so that they don't clash with other systems.
We should probably have a specialized set of rules and a special
checklist for this interface.
_`.fmt.extend`: This paragraph should be an explanation of why
``mps_fmt_A_t`` is so called. The underlying reason is future
extensibility.
_`.thread-safety`: Most calls through this interface lock the space
and therefore make the MPM single-threaded. In order to do this they
must recover the space from their parameters. Methods such as
``ThreadSpace()`` must therefore be callable when the space is *not*
locked. These methods are tagged with the tag of this note.
_`.lock-free`: Certain functions inside the MPM are thread-safe and do
not need to be serialized by using locks. They are marked with the tag
of this note.
_`.form`: Almost all functions in this implementation simply cast
their arguments to the equivalent internal types, and cast results
back to the external type, where necessary. Only exceptions are noted
in comments.
Document History
----------------
- 1996-07-29 RB_ Incomplete document. The first draft of this document
was generated in response to review.impl.h.mps.10 which revealed the
lack of a detailed design document and also the lack of conventions
for external interfaces. The aim of the draft was to record this
information, even if it isn't terribly well structured.
- 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.**

465
mps/design/io.txt Normal file
View file

@ -0,0 +1,465 @@
.. mode: -*- rst -*-
I/O subsystem
=============
:Tag: design.mps.io
:Author: Richard Brooksby
:Date: 1996-08-30
:Status: incomplete design
:Revision: $Id$
:Copyright: See `Copyright and License`_.
Introduction
------------
_`.intro`: This document is the design of the MPS I/O Subsystem, a
part of the plinth.
_`.readership`: This document is intended for MPS developers.
Background
----------
_`.bg`: This design is partly based on the design of the Internet User
Datagram Protocol (UDP). Mainly I used this to make sure I hadn't left
out anything which we might need.
Purpose
-------
_`.purpose`: The purpose of the MPS I/O Subsystem is to provide a
means to measure, debug, control, and test a memory manager build
using the MPS.
_`.purpose.measure`: Measurement consists of emitting data which can
be collected and analysed in order to improve the attributes of
application program, quite possibly by adjusting parameters of the
memory manager (see overview.mps.usage).
_`.purpose.control`: Control means adjusting the behaviour of the MM
dynamically. For example, one might want to adjust a parameter in
order to observe the effect, then transfer that adjustment to the
client application later.
_`.purpose.test`: Test output can be used to ensure that the memory
manager is behaving as expected in response to certain inputs.
Requirements
------------
General
.......
_`.req.fun.non-hosted`: The MPM must be a host-independent system.
_`.req.attr.host`: It should be easy for the client to set up the MPM
for a particular host (such as a washing machine).
Functional
..........
_`.req.fun.measure`: The subsystem must allow the MPS to transmit
quantitative measurement data to an external tool so that the system
can be tuned.
_`.req.fun.debug`: The subsystem must allow the MPS to transmit
qualitative information about its operation to an external tool so
that the system can be debugged.
_`.req.fun.control`: The subsystem must allow the MPS to receive
control information from an external tool so that the system can be
adjusted while it is running.
_`.req.dc.env.no-net`: The subsystem should operate in environments
where there is no networking available.
_`.req.dc.env.no-fs`: The subsystem should operate in environments
where there is no filesystem available.
Architecture
------------
_`.arch.diagram`: I/O Architecture Diagram
[missing diagram]
_`.arch.int`: The I/O Interface is a C function call interface by
which the MPM sends and receives "messages" to and from the hosted I/O
module.
_`.arch.module`: The modules are part of the MPS but not part of the
freestanding core system (see design.mps.exec-env). The I/O module is
responsible for transmitting those messages to the external tools, and
for receiving messages from external tools and passing them to the
MPM.
_`.arch.module.example`: For example, the "file implementation" might
just send/write telemetry messages into a file so that they can be
received/read later by an off-line measurement tool.
_`.arch.external`: The I/O Interface is part of interface to the
freestanding core system (see design.mps.exec-env). This is so that
the MPS can be deployed in a freestanding environment, with a special
I/O module. For example, if the MPS is used in a washing machine the
I/O module could communicate by writing output to the seven-segment
display.
Example configurations
......................
_`.example.telnet`: This shows the I/O subsystem communicating with a
telnet client over a TCP/IP connection. In this case, the I/O
subsystem is translating the I/O Interface into an interactive text
protocol so that the user of the telnet client can talk to the MM.
[missing diagram]
_`.example.file`: This shows the I/O subsystem dumping measurement
data into a file which is later read and analysed. In this case the
I/O subsystem is simply writing out binary in a format which can be
decoded.
[missing diagram]
_`.example.serial`: This shows the I/O subsystem communicating with a
graphical analysis tool over a serial link. This could be useful for a
developer who has two machines in close proximity and no networking
support.
_`.example.local`: In this example the application is talking directly
to the I/O subsystem. This is useful when the application is a
reflective development environment (such as MLWorks) which wants to
observe its own behaviour.
[missing diagram]
Interface
---------
_`.if.msg`: The I/O interface is oriented around opaque binary
"messages" which the I/O module must pass between the MPM and external
tools. The I/O module need not understand or interpret the contents of
those messages.
_`.if.msg.opaque`: The messages are opaque in order to minimize the
dependency of the I/O module on the message internals. It should be
possible for clients to implement their own I/O modules for unusual
environments. We do not want to reveal the internal structure of our
data to the clients. Nor do we want to burden them with the details of
our protocols. We'd also like their code to be independent of ours, so
that we can expand or change the protocols without requiring them to
modify their modules.
_`.if.msg.dgram`: Neither the MPM nor the external tools should assume
that the messages will be delivered in finite time, exactly once, or
in order. This will allow the I/O modules to be implemented using
unreliable transport layers such as the Internet User Datagram Protocl
(UDP). It will also give the I/O module the freedom to drop
information rather than block on a congested network, or stop the
memory manager when the disk is full, or similar events which really
shouldn't cause the memory manager to stop working. The protocols we
need to implement at the high level can be design to be robust against
lossage without much difficulty.
I/O module state
................
_`.if.state`: The I/O module may have some internal state to preserve.
The I/O Interface defines a type for this state, ``mps_io_t``, a
pointer to an incomplete structure ``mps_io_s``. The I/O module is at
liberty to define this structure.
Message types
.............
_`.if.type`: The I/O module must be able to deliver messages of
several different types. It will probably choose to send them to
different destinations based on their type: telemetry to the
measurement tool, debugging output to the debugger, etc. ::
typedef int mps_io_type_t;
enum {
MPS_IO_TYPE_TELEMETRY,
MPS_IO_TYPE_DEBUG
};
Limits
......
_`.if.message-max`: The interface will define an unsigned integral
constant ``MPS_IO_MESSAGE_MAX`` which will be the maximum size of
messages that the MPM will pass to ``mps_io_send()`` (`.if.send`_) and
the maximum size it will expect to receive from ``mps_io_receive()``.
Interface set-up and tear-down
..............................
_`.if.create`: The MPM will call ``mps_io_create()`` to set up the I/O
module. On success, this function should return ``MPS_RES_OK``. It may
also initialize a "state" value which will be passed to subsequent
calls through the interface.
_`.if.destroy`: The MPM will call ``mps_io_destroy()`` to tear down
the I/O module, after which it guarantees that the state value will
not be used again. The ``state`` parameter is the state previously
returned by ``mps_io_create()`` (`.if.create`_).
Message send and receive
........................
``extern mps_res_t mps_io_send(mps_io_t state, mps_io_type_t type, void *message, size_t size)``
_`.if.send`: The MPM will call ``mps_io_send()`` when it wishes to
send a message to a destination. The ``state`` parameter is the state
previously returned by ``mps_io_create()`` (`.if.create`_). The
``type`` parameter is the type (`.if.type`_) of the message. The
``message`` parameter is a pointer to a buffer containing the message,
and ``size`` is the length of that message, in bytes. The I/O module
must make an effort to deliver the message to the destination, but is
not expected to guarantee delivery. The function should return
``MPS_RES_IO`` only if a serious error occurs that should cause the
MPM to return with an error to the client application. Failure to
deliver the message does not count.
.. note::
Should there be a timeout parameter? What are the timing
constraints? ``mps_io_send()`` shouldn't block.
``extern mps_res_t mps_io_receive(mps_io_t state, void **buffer_o, size_t *size_o)``
_`.if.receive`: The MPM will call ``mps_io_receive()`` when it wants
to see if a message has been sent to it. The ``state`` parameter is
the state previously returned by ``mps_io_create()`` (`.if.create`_).
The ``buffer_o`` parameter is a pointer to a value which should be
updated with a pointer to a buffer containing the message received.
The ``size_o`` parameter is a pointer to a value which should be
updated with the length of the message received. If there is no
message ready for receipt, the length returned should be zero.
.. note::
Should we be able to receive truncated messages? How can this be
done neatly?
I/O module implementations
--------------------------
Routeing
........
The I/O module must decide where to send the various messages. A
file-based implementation could put them in different files based on
their types. A network-based implementation must decide how to address
the messages. In either case, any configuration must either be
statically compiled into the module, or else read from some external
source such as a configuration file.
Notes
-----
The external tools should be able to reconstruct stuff from partial
info. For example, you come across a fragment of an old log containing
just a few old messages. What can you do with it?
Here's some completely untested code which might do the job for UDP.
::
#include "mpsio.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
typedef struct mps_io_s {
int sock;
struct sockaddr_in mine;
struct sockaddr_in telemetry;
struct sockaddr_in debugging;
} mps_io_s;
static mps_bool_t inited = 0;
static mps_io_s state;
mps_res_t mps_io_create(mps_io_t *mps_io_o)
{
int sock, r;
if(inited)
return MPS_RES_LIMIT;
state.mine = /* setup somehow from config */;
state.telemetry = /* setup something from config */;
state.debugging = /* setup something from config */;
/* Make a socket through which to communicate. */
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(sock == -1) return MPS_RES_IO;
/* Set socket to non-blocking mode. */
r = fcntl(sock, F_SETFL, O_NDELAY);
if(r == -1) return MPS_RES_IO;
/* Bind the socket to some UDP port so that we can receive messages. */
r = bind(sock, (struct sockaddr *)&state.mine, sizeof(state.mine));
if(r == -1) return MPS_RES_IO;
state.sock = sock;
inited = 1;
*mps_io_o = &state;
return MPS_RES_OK;
}
void mps_io_destroy(mps_io_t mps_io)
{
assert(mps_io == &state);
assert(inited);
(void)close(state.sock);
inited = 0;
}
mps_res_t mps_io_send(mps_io_t mps_io, mps_type_t type,
void *message, size_t size)
{
struct sockaddr *toaddr;
assert(mps_io == &state);
assert(inited);
switch(type) {
MPS_IO_TYPE_TELEMETRY:
toaddr = (struct sockaddr *)&state.telemetry;
break;
MPS_IO_TYPE_DEBUGGING:
toaddr = (struct sockaddr *)&state.debugging;
break;
default:
assert(0);
return MPS_RES_UNIMPL;
}
(void)sendto(state.sock, message, size, 0, toaddr, sizeof(*toaddr));
return MPS_RES_OK;
}
mps_res_t mps_io_receive(mps_io_t mps_io,
void **message_o, size_t **size_o)
{
int r;
static char buffer[MPS_IO_MESSAGE_MAX];
assert(mps_io == &state);
assert(inited);
r = recvfrom(state.sock, buffer, sizeof(buffer), 0, NULL, NULL);
if(r == -1)
switch(errno) {
/* Ignore interrupted system calls, and failures due to lack */
/* of resources (they might go away.) */
case EINTR: case ENOMEM: case ENOSR:
r = 0;
break;
default:
return MPS_RES_IO;
}
*message_o = buffer;
*size_o = r;
return MPS_RES_OK;
}
Attachments
-----------
"O Architecture Diagram"
"O Configuration Diagrams"
Document History
----------------
- 1996-08-30 RB_ Document created from paper notes.
- 1997-06-10 RB_ Updated with mail.richard.1997-05-30.16-13 and
subsequent discussion in the Pool Hall at Longstanton. (See also
mail.drj.1997-06-05.15-20.)
- 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.**

733
mps/design/locus.txt Normal file
View file

@ -0,0 +1,733 @@
.. mode: -*- rst -*-
MPS Configuration
=================
:Tag: design.mps.locus
:Author: Gavin Matthews
:Date: 1998-02-27
:Status: incomplete design
:Revision: $Id$
:Copyright: See `Copyright and License`_.
Introduction
------------
_`.intro`: The locus manager coordinates between the pools and takes
the burden of having to be clever about tract/group placement away
from the pools, preserving trace differentiability and contiguity
where appropriate.
_`.source`: mail.gavinm.1998-02-05.17-52(0), mail.ptw.1998-02-05.19-53(0),
mail.pekka.1998-02-09.13-58(0), and mail.gavinm.1998-02-09.14-05(0).
_`.readership`: Any MPS developer.
Overview
--------
The MPS manages three main resources:
1. storage;
2. address space;
3. time.
The locus manager manages address space at the arena level.
.. note:
Tucker was right: see mail.ptw.1998-11-02.14-25. Richard Kistruck,
2007-04-24.
When a pool wants some address space, it expresses some preferences to
the locus manager. The locus manager and the arena (working together)
try to honour these preferences, and decide what address space the
pool gets.
Preferences are expressed by the ``SegPref`` argument to
``SegAlloc()``. Note that, when they call ``SegAlloc()``, pools are
asking for address space and writeable storage simultaneously, in a
single call. There is currently no way for pools to reserve address
space without requesting storage.
Why is it important to manage address space?
............................................
1. Trace differentiability
Carefully chosen addresses are used by reference tracing systems
(ie. automatic pools), to categorise objects into clumps; and to
summarise and cheaply find references between clumps.
Different clumps will become worth collecting at different times
(the classic example, of course, is generations in a generational
collector). For these partial collections to be efficient, it must
be cheap to keep these clumps differentiable, cheap to condemn
(Whiten) a particular clump, and cheap to find a good conservative
approximation to all inward references to a clump (both initially
to construct the Grey set, and to make scanning the Grey set
efficient).
This is what the MPS zone mechanism is all about.
The locus manager manages the mapping from clumps to zones.
To specify a clump, pools use the ``SegPrefGen`` argument to
``SegPrefExpress()``.
.. note::
The name is misleading: generations are only one sort of clump.
Richard Kistruck, 2007-04-24.
2. Prevent address space fragmentation (within the arena)
Address space is not infinite.
In some use cases, the MPS is required to remain efficient when
using very nearly all available address space and storage. For
example, with the client-arena class, where the only address space
available is that of the storage available.
Even with the VM arena class, typical storage sizes (as of 2007)
can make 32-bit address space constrained: the client may need
several gigabytes, which leaves little spare address space.
Address space fragmentation incurs failure when there is no way to
allocate a big block of address space. The big block may be
requested via the MPS (by the client), or by something else in the
same process, such as a third-party graphics library, image
library, etc.
Address space fragmentation incurs cost when:
- desired large-block requests (such as for buffering) are denied,
causing them to be re-requested as a smaller block, or as several
smaller blocks;
- possible operating-system costs in maintaining a fragmented
mapping?
3. Prevent storage fragmentation (within tracts and segments)
Storage is not infinite: it is allocated in multiples of a
fixed-size tract. Small lonely objects, each retaining a whole
tract, cause storage fragmentation.
Non-moving pools manage this fragmentation with placement
strategies that use:
- co-located death (in space and time);
- segment merging and splitting.
These pool-level strategies always care about contiguity of object
storage. They also often care about the *ordering* of addresses,
because pool code uses an address-ordered search when choosing
where to place a new object. For these two reasons, the address
chosen (by the locus manager and arena) for new tracts is
important.
Certain specialised pools, and/or some client programs that use
them, have carefully tuned segment sizes, positioning, and search
order. Be careful: seemingly inconsequential changes can
catastrophically break this tuning.
Pools can specify a preference for High and Low ends of address
space, which implies a search-order. Pools could also specify
clumping, using either ``SegPrefGen`` or ``SegPrefZoneSet``.
Discovering the layout
......................
The locus manager is not given advance notice of how much address
space will be required with what preferences. Instead, the locus
manager starts with an empty layout, and adapts it as more requests
come in over time. It is attempting to discover a suitable layout by
successive refinement. This is ambitious.
Definitions
-----------
_`.note.cohort`: We use the word "cohort" in its usual sense here, but
we're particularly interested in cohorts that have properties relevant
to tract placement. It is such cohorts that the pools will try to
organize using the services of the locus manager. Typical properties
would be trace differentiability or (en masse) death-time
predictability. Typical cohorts would be instances of a
non-generational pool, or generations of a collection strategy.
_`.def.trace.differentiability`: Objects (and hence tracts) that are
collected, may or may not have "trace differentiability" from each
other, depending on their placement in the different zones. Objects
(or pointers to them) can also have trace differentiability (or not)
from non-pointers in ambiguous references; in practice, we will be
worried about low integers, that may appear to be in zones 0 or -1.
Requirements
------------
_`.req.cohort`: Tract allocations must specify the cohort they
allocate in. These kind of cohorts will be called loci, and they will
have such attributes as are implied by the other requirements.
Critical.
_`.req.counter.objects`: As a counter-requirement, pools are expected
to manage objects. Objects the size of a tract allocation request
(segment-sized) are exceptional. Critical.
_`.req.counter.objects.just`: This means the locus manager is not
meant to solve the problems of allocating large objects, and it isn't
required to know what goes on in pools.
_`.req.contiguity`: Must support a high level of contiguity within
cohorts when requested. This means minimizing the number of times a
cohort is made aware of discontiguity. Essential (as we've effectively
renegotiated this in SW, down to a vague hope that certain critical
cohorts are not too badly fragmented). _`.req.contiguity.just`: TSBA.
_`.req.contiguity.specific`: It should be possible to request another
allocation next to a specific tract on either side (or an extension in
that direction, as the case may be). Such a request can fail, if
there's no space there. Nice. It would also be nice to have one for
"next to the largest free block".
_`.req.differentiable`: Must support the trace differentiability of
segments that may be condemned separately. Due to the limited number
of zones, it must be possible to place several cohorts into the same
zone. Essential.
_`.req.differentiable.integer`: It must be possible to place
collectable allocations so that they are trace-differentiable from
small integers. Essential.
_`.req.disjoint`: Must support the disjointness of pages that have
different VM properties (such as mutable/immutable,
read-only/read-write, and different lifetimes). Optional.
.. note::
I expect the implementation will simply work at page or larger
granularity, so the problem will not arise, but Tucker insisted on
stating this as a requirement. Pekka P. Pirinen, 1998-10-28.
_`.req.low-memory`: The architecture of the locus manager must not
prevent the design of efficient applications that often use all
available memory. Critical. _`.req.low-memory.expl`: This basically
says it must be designed to perform well in low-memory conditions, but
that there can be configurations where it doesn't do as well, as long
as this is documented for the application programmer. Note that it
doesn't say all applications are efficient, only that if you manage to
design an otherwise efficient application, the locus manager will not
sink it.
_`.req.address`: Must conserve address space in VM arenas to a
reasonable extent. Critical.
_`.req.inter-pool`: Must support the association of sets of tracts in
different pools into one cohort. Nice.
_`.req.ep-style`: Must support the existing EP-style of allocation
whereby allocation is from one end of address space either upwards or
downwards (or a close approximation thereto with the same behavior).
_`.req.ep-style.just`: We cannot risk disrupting a policy with
well-known properties when this technology is introduced.
_`.req.attributes`: There should be a way to inform the locus manager
about various attributes of cohorts that might be useful for
placement: deathtime, expected total size, and so on. Optional. It's a
given that the cohorts must then have these attributes, within the
limits set in the contract of the appropriate interface.
_`.req.attributes.action`: The locus manager should use the attributes
to guide its placement decisions. Nice.
_`.req.blacklisting`: There should be a way of maintaining at least
one blacklist for pages (or some other small unit), that can
not/should not be allocated to collectable pools. Optional.
.. note::
How to do blacklist breaking for ambiguous refs?
_`.req.hysteresis`: There should be a way to indicate which cohorts
fluctuate in size and by how much, to guide the arena hysteresis to
hold on to suitable pages. Optional.
Analysis
--------
_`.anal.sw`: Almost any placement policy would be an improvement on
the current SW one.
_`.anal.cause-and-effect`: The locus manager doesn't usually need to
know *why* things need to be differentiable, disjoint, contiguous, and
so on. Abstracting the reason away from the interface makes it more
generic, more likely to have serendipitous new uses. Attributes
described by a quantity (deathtime, size, etc.) are an exception to
this, because we can't devise a common measure.
_`.anal.stable`: The strategy must be stable: it must avoid repeated
recomputation, especially the kind that switches between alternatives
with a short period (repeated "bites" out the same region or
flip-flopping between two regions).
_`.anal.fragmentation`: There's some call to avoid fragmentation in
cohorts that don't need strict contiguity, but this is not a separate
requirement, since fragmentation is a global condition, and can only
be ameliorated if there's a global strategy that clumps allocations
together.
_`.anal.deathtime`: Cohorts with good death-time clumping of their
objects could use some locality of tract allocation, because it
increases the chances of creating large holes in the address space
(for other allocation to use). OTOH. many cohorts will not do multiple
frees in short succession, or at least cannot reasonably be predicted
to do so. This locality is not contiguity, nor is it low
fragmentation, it's just the requirement to place the new tracts next
to the tract where the last object was allocated in the cohort. Note
that the placement of objects is under the control of the pool, and
the locus manager will not know it, therefore this requirement should
be pursued by requesting allocation next to a particular tract (which
we already have a requirement for).
_`.anal.asymmetrical`: The strategy has to be asymmetrical with
respect to cohorts growing and shrinking. The reason of this asymmetry
is that it can choose where to grow, but it cannot choose where to
shrink (except in a small way by growing with good locality).
Interface
---------
_`.interface.locus`: A cohort will typically reside on multiple tracts
(and the pools will avoid putting objects of other cohorts on them),
so there should be an interface to describe the properties of the
cohort, and associate each allocation request with the cohort. We
shall call such an object, created to represent a cohort, a locus (pl.
loci).
_`.interface.locus.pool`: Loci will usually be created by the pool
that uses it. Some of the locus attributes will be inherited from
client-specified pool attributes [this means there will be additional
pool attributes].
_`.interface.detail`: This describes interface in overview; for
details, see implementation section and code, or user doc.
Loci
....
``Res LocusCreate(Locus *locusReturn, LocusAttrs attrs, ZoneGroup zg,
LocusAllocDesc adesc)``
_`.function.create`: A function to create a locus: ``adesc`` contains
the information about the allocation sequences in the locus, ``zg`` is
used for zone differentiability, and ``attrs`` encodes the following:
- _`.locus.contiguity`: A locus can be contiguous. This means
performing as required in `.req.contiguity`_, non-contiguous
allocations can be freely placed anywhere (but efficiency dictates
that similar allocations are placed close together and apart from
others).
- _`.locus.blacklist`: Allocations in the locus will avoid blacklisted
pages (for collectable segments).
- _`.locus.zero`: Allocations in the locus are zero-filled.
.. note::
Other attributes will be added, I'm sure.
_`.interface.zone-group`: The locus can be made a member of a zone
group. Passing ``ZoneGroupNONE`` means it's not a member of any group
(allocations will be placed without regard to zone, except to keep
them out of stripes likely to be needed for some group).
.. note::
I propose no mechanism for managing zone groups at this time,
since it's only used internally for one purpose. Pekka P. Pirinen,
2000-01-17.
_`.interface.size`: An allocation descriptor (``LocusAllocDesc``)
contains various descriptions of how the locus will develop over time
(inconsistent specifications are forbidden, of course):
- _`.interface.size.typical-alloc`: Size of a typical allocation in
this locus, in bytes. This will mainly affect the grouping of
non-contiguous loci.
- _`.interface.size.large-alloc`: Typical large allocation that the
manager should try to allow for (this allows some relief from
`.req.counter.objects`_), in bytes. This will mainly affect the size
of gaps that will be allotted adjoining this locus.
- _`.interface.size.direction`: Direction of growth: up/down/none.
Only useful if the locus is contiguous.
- _`.interface.size.lifetime`: Some measure of the lifetime of tracts
(not objects) in the cohort.
.. note::
Don't know the details yet, probably only useful for placing
similar cohorts next to each other, so the details don't
actually matter. Pekka P. Pirinen, 2000-01-17.
- _`.interface.size.deathtime`: Some measure of the deathtime of
tracts (not objects) in the cohort.
.. note::
Ditto. Pekka P. Pirinen, 2000-01-17.
_`.function.init`: ``LocusInit()`` is like ``LocusCreate()``, but
without the allocation. This is the usual interface, since most loci
are embedded in a pool or something.
_`.function.alloc`: ``ArenaAlloc()`` to take a locus argument.
``ArenaAllocHere()`` is like it, plus it takes a tract and a
specification to place the new allocation immediately above/below a
given tract; if that is not possible, it returns ``ResFAIL`` (this
will make it useful for reallocation functionality).
``ArenaSetTotalLoci(Arena arena, Size nLoci, Size nZoneGroups)``
_`.function.set-total`: A function to tell the arena the expected
number of (non-miscible client) loci, and of zone groups.
Peaks
.....
``mps_res_t mps_peak_create(mps_peak_t*, mps_arena_t)``
_`.function.peak.create`: A function to create a peak. A newly-created
peak is open, and will not be used to guide the strategy of the locus
manager.
``mps_res_t mps_peak_describe_pool(mps_peak_t, mps_pool_t, mps_size_desc_t)``
_`.function.peak.add`: A function to add a description of the state of
one pool into the peak. Calling this function again for the same peak and pool instance will replace
the earlier description.
_`.function.peak.add.size`: The size descriptor contains a total size
in bytes or percent of arena size.
.. note::
Is this right? Pekka P. Pirinen, 2000-01-17.
_`.function.peak.add.remove`: Specifying a ``NULL`` size will remove
the pool from the peak. The client is not allowed to destroy a pool
that is mentioned in any peak; it must be first removed from the peak,
or the peak must be destroyed. This is to ensure that the client
adjusts the peaks in a manner that makes sense to the application; the
locus manager can't know how to do that.
``mps_res_t mps_peak_close(mps_peak_t)``
_`.function.peak.close`: A function to indicate that all the
significant pools have been added to the peak, and it can now be used
to guide the locus manager. For any pool not described in the peak,
the locus manager will take its current size at any given moment as
the best prediction of its size at the peak.
_`.function.peak.close.after`: It is legal to add more descriptions to
the peak after closing, but this will reopen the peak, and it will
have to be closed before the locus manager will use it again. The
locus manager uses the previous closed state of the peak, while this
is going on.
``void mps_peak_destroy(mps_peak_t)``
_`.function.peak.destroy`: A function to destroy a peak.
_`.interface.ep-style`: This satisfies `.req.ep-style`_ by allowing SW
to specify zero size for most pools (which will cause them to be place
next to other loci with the same growth direction).
.. note::
Not sure this is good enough, but we'll try it first. Pekka P.
Pirinen, 2000-01-17.
Architecture
------------
Data objects
............
_`.arch.locus`: To represent the cohorts, we have locus objects.
Usually a locus is embedded in a pool instance, but generations are
separate loci.
_`.arch.locus.attr`: contiguity, blacklist, zg, current region, @@@@
_`.arch.locus.attr.exceptional`: The client can define a typical large
allocation for the locus. Requests substantially larger than that are
deemed exceptional.
_`.arch.zone-group`: To satisfy `.req.condemn`_, we offer zone groups.
Each locus can be a member of a zone group, and the locus manager will
attempt to place allocations in this locus in different zones from all
the other zone groups. A zone-group is represented as @@@@.
_`.arch.page-table`: A page table is maintained by the arena, as usual
to track association between tracts, pools and segments, and mapping
status for VM arenas.
_`.arch.region`: All of the address space is divided into disjoint
regions, represented by region objects. These objects store their
current limits, and high and low watermarks of currently allocated
tracts (we hope there's usually a gap of empty space between regions).
The limits are actually quite porous and flexible.
_`.arch.region.assoc`: Each region is associated with one contiguous
locus or any number of non-contiguous loci (or none). We call the
first kind of region "contiguous". _`.arch.locus.assoc`: Each locus
remembers all regions where it has tracts currently, excepting the
badly-placed allocations (see below). It is not our intention that any
locus would have very many, or that loci that share regions would have
any reason to stop doing do.
_`.arch.region.more`: Various quantities used by the placement
computation are also stored in the regions and the loci. Regions are
created (and destroyed) by the placement recomputation. Regions are
located in stripes (if it's a zoned region), but they can extend into
neighboring stripes if an exceptionally large tract allocation is
requested (to allow for large objects).
_`.arch.chunk`: Arenas may allocate more address space in additional
chunks, which may be disjoint from the existing chunks. Inter-chunk
space will be represented by dummy regions. There are also sentinel
regions at both ends of the address space.
Overview of strategy
....................
_`.arch.strategy.delay`: The general strategy is to delay placement
decisions until they have to be made, but no later.
_`.arch.strategy.delay.until`: Hence, the locus manager only makes
placement decisions when an allocation is requested (frees and other
operations might set a flag to cause the next allocation to redecide).
This also allows the client to change the peak and pool configuration
in complicated ways without causing a lot of recomputation, by doing
all the changes without allocating in the middle (unless the control
pool needs more space because of the changes).
_`.arch.strategy.normal`: While we want the placement to be
sophisticated, we do not believe it is worth the effort to consider
all the data at each allocation. Hence, allocations are usually just
placed in one of the regions used previously (see `.arch.alloc`_)
without reconsidering the issues.
_`.arch.strategy.normal.limit`: However, the manager sets
precautionary limits on the regions to ensure that the placement
decisions are revisited when an irrevocable placement is about to be
made.
_`.arch.strategy.create`: The manager doesn't create new regions until
they are needed for allocation (but it might compute where they could
be placed to accommodate a peak).
Allocation
..........
_`.arch.alloc`: Normally, each allocation to a locus is placed in its
current region. New regions are only sought when necessary to fulfill
an allocation request or when there is reason to think the situation
has changed significantly (see `.arch.significant`_).
_`.arch.alloc.same`: An allocation is first attempted next to the
previous allocation in the same locus, respecting growth direction. If
that is not possible, a good place in the current region is sought.
_`.arch.alloc.same.hole`: At the moment, for finding a good place
within a region, we just use the current algorithm, limited to the
region. In future, the placement within regions will be more clever.
_`.arch.alloc.extend`: If there's no adequate hole in the current
region and the request is not exceptional, the neighboring regions are
examined to see if the region could be extended at one border. (This
will basically only be done if the neighbor has shrunk since the last
placement recomputation, because the limit was set on sophisticated
criteria, and should not be changed without justification.)
_`.arch.alloc.extend.here`: When an allocation is requested next to a
specific tract (``ArenaAllocHere()``), we try to extend a little
harder (at least for ``change_size``, perhaps not for locality).
_`.arch.alloc.other`: If no way can be found to allocate in the
current region, other regions used for this locus are considered in
the same way, to see if space can be found there. [Or probably look at
other regions before trying to extend anything?]
_`.arch.alloc.recompute`: When no region of this locus has enough
space for the request, or when otherwise required, region placement is
recomputed to find a new region for the request (which might be the
same region, after extension).
_`.arch.alloc.current`: This region where the allocation was placed
then becomes the current region for this locus, except when the
request was exceptional, or when the region chosen was "bad" (see
@@@@).
_`.arch.significant`: Significant changes to the parameters affecting
placement are deemed to have happened at certain client calls and when
the total allocation has changed substantially since the last
recomputation. Such conditions set a flag that causes the next
allocation to recompute even if its current region is not full
(possibly second-guess the decision to recompute after some
investigation of the current state?).
Deallocation
............
_`.arch.free`: Deallocation simply updates the counters in the region
and the locus. For some loci, it will make the region of the
deallocation the current region. _`.arch.free.remove`: If a region
becomes entirely empty, it is deleted (and the neighbors limits might
be adjusted).
.. note::
This is quite tricky to get right.
Region placement recomputation
..............................
_`.arch.gap`: When doing placement computations, we view the arena as
a sequence of alternating region cores and gaps (which can be small,
even zero-sized). Initially, we'll take the core of a region to be the
area between the high and low watermark, but in the future we might be
more flexible about that.
.. note::
Edge determination is actually a worthwhile direction to explore.
_`.arch.reach`: The gap between two cores could potentially end up
being allocated to either region, if they grow in that direction, or
one or neither, if they don't. The set of states that the region
assignment could reach by assigning the gaps to their neighbors is
called the reach of the current configuration.
_`.arch.placement.object`: The object of the recomputation is to find
a configuration of regions that is not too far from the current
configuration and that keeps all the peaks inside its reach; if that
is not possible, keep the nearest ones in the reach and then minimize
the total distance from the rest.
_`.arch.placement.hypothetical`: The configurations that are
considered will include hypothetical placements for new regions for
loci that cannot fit in their existing regions at the peak. This is
necessary to avoid choosing a bad alternative.
_`.arch.placement.interesting`: The computation will only consider new
regions of loci that are deemed interesting, that is, far from their
peak state. This will reduce the computational burden and avoid
jittering near a peak.
.. note::
Details missing.
Implementation
--------------
[missing]
Notes
-----
_`.idea.change`: Even after the first segment, be prepared to change
your mind, if by the second segment a lot of new loci have been
created.
_`.distance`: If the current state is far from a peak, there's time to
reassign regions and for free space to appear (in fact, under the
steady arena assumption, enough free space *will* appear).
_`.clear-pool`: Need to have a function to deallocate all objects in a
pool, so that ``PoolDestroy()`` won't have to be used for that
purpose.
Document History
----------------
- 1998-02-27 Gavin Matthews. Incomplete design. Originally written as
part of change.dylan.box-turtle.170569. Much developed since.
- 1998-10-28 Pekka P. Pirinen. Wrote the real requirements after some
discussion.
- 1998-12-15 Pekka P. Pirinen. Deleted Gavin's design and wrote a new one.
- 2002-06-07 RB_ Converted from MMInfo database design document.
- 2007-04-24 Richard Kistruck. Added Guide: Manage arena address
space, why, discover layout.
- 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

@ -50,22 +50,18 @@ exists. It satisfies `.req.test`_ and `.req.rapid-port`_ in this way.
Functions
---------
``void ProtSetup(void)``
_`.fun.protsetup`: ``ProtSetup()`` does nothing as there is nothing to
do (under UNIX we might expect the protection module to install one or
more signal handlers at this pointer, but that is not appropriate for
the ANSI implementation). Of course, we can't have an empty function
body, so there is a ``NOOP;`` here.
_`.fun.protsetup`: Does nothing as there is nothing to do (under UNIX
we might expect the protection module to install one or more signal
handlers at this pointer, but that is not appropriate for the ANSI
implementation). Of course, we can't have an empty function body, so
there is a ``NOOP;`` here.
``void ProtSync(Arena arena)``
_`.fun.sync`: Called to ensure that the actual protection of each
segment (as determined by the OS) is in accordance with the segments's
pm field. In the ANSI implementation we have no way of changing the
protection of a segment, so instead we generate faults on all
protected segments in the assumption that that will remove the
protection on segments.
_`.fun.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. In the ANSI implementation we have no
way of changing the protection of a segment, so instead we generate
faults on all protected segments in the assumption that that will
remove the protection on segments.
_`.fun.sync.how`: Continually loops over all the segments until it
finds that all segments have no protection.

View file

@ -0,0 +1,6 @@
.. index::
pair: allocation frames; design
.. _design-alloc-frame:
.. include:: ../../converted/alloc-frame.rst

View file

@ -0,0 +1,6 @@
.. index::
pair: diagnostic feedback; design
.. _design-diag:
.. include:: ../../converted/diag.rst

View file

@ -0,0 +1,7 @@
.. index::
pair: C language; formatting guide
pair: C language formatting; guide
.. _design-guide.impl.c.format:
.. include:: ../../converted/guide.impl.c.format.rst

View file

@ -9,6 +9,7 @@ Design
config
critical-path
guide.hex.trans
guide.impl.c.format
keyword-arguments
ring
sig

View file

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

View file

@ -0,0 +1,6 @@
.. index::
pair: I/O subsystem; design
.. _design-io:
.. include:: ../../converted/io.rst

View file

@ -0,0 +1,6 @@
.. index::
pair: locus manager; design
.. _design-locus:
.. include:: ../../converted/locus.rst

View file

@ -14,6 +14,7 @@ Old design
.. toctree::
:numbered:
alloc-frame
arena
arenavm
bt
@ -22,10 +23,14 @@ Old design
check
class-interface
collection
diag
finalize
fix
interface-c
io
lib
lock
locus
message
message-gc
object-debug

View file

@ -18,7 +18,7 @@ TYPES = '''
def main():
mode = re.compile(r'\.\. mode: .*\n')
prefix = re.compile(r'^:Tag: ([a-z][a-z.0-9-]*[a-z0-9])$')
rst_tag = re.compile(r'^:(?:Author|Date|Status|Revision|Copyright|Organization):.*\n')
rst_tag = re.compile(r'^:(?:Author|Date|Status|Revision|Copyright|Organization|Format):.*\n')
mps_tag = re.compile(r'_`\.([a-z][A-Za-z.0-9_-]*[A-Za-z0-9])`:')
mps_ref = re.compile(r'`(\.[a-z][A-Za-z.0-9_-]*[A-Za-z0-9])`_(?: )?')
funcdef = re.compile(r'^``([^`]*\([^`]*\))``$')