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:
parent
b3ad8d80b3
commit
273bfaa70b
16 changed files with 2513 additions and 17 deletions
513
mps/design/alloc-frame.txt
Normal file
513
mps/design/alloc-frame.txt
Normal 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
355
mps/design/diag.txt
Normal 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.**
|
||||
|
|
@ -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
389
mps/design/interface-c.txt
Normal 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
465
mps/design/io.txt
Normal 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
733
mps/design/locus.txt
Normal 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.**
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
6
mps/manual/source/design/alloc-frame.rst
Normal file
6
mps/manual/source/design/alloc-frame.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.. index::
|
||||
pair: allocation frames; design
|
||||
|
||||
.. _design-alloc-frame:
|
||||
|
||||
.. include:: ../../converted/alloc-frame.rst
|
||||
6
mps/manual/source/design/diag.rst
Normal file
6
mps/manual/source/design/diag.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.. index::
|
||||
pair: diagnostic feedback; design
|
||||
|
||||
.. _design-diag:
|
||||
|
||||
.. include:: ../../converted/diag.rst
|
||||
7
mps/manual/source/design/guide.impl.c.format.rst
Normal file
7
mps/manual/source/design/guide.impl.c.format.rst
Normal 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
|
||||
|
|
@ -9,6 +9,7 @@ Design
|
|||
config
|
||||
critical-path
|
||||
guide.hex.trans
|
||||
guide.impl.c.format
|
||||
keyword-arguments
|
||||
ring
|
||||
sig
|
||||
|
|
|
|||
6
mps/manual/source/design/interface-c.rst
Normal file
6
mps/manual/source/design/interface-c.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.. index::
|
||||
pair: C interface; design
|
||||
|
||||
.. _design-interface-c:
|
||||
|
||||
.. include:: ../../converted/interface-c.rst
|
||||
6
mps/manual/source/design/io.rst
Normal file
6
mps/manual/source/design/io.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.. index::
|
||||
pair: I/O subsystem; design
|
||||
|
||||
.. _design-io:
|
||||
|
||||
.. include:: ../../converted/io.rst
|
||||
6
mps/manual/source/design/locus.rst
Normal file
6
mps/manual/source/design/locus.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.. index::
|
||||
pair: locus manager; design
|
||||
|
||||
.. _design-locus:
|
||||
|
||||
.. include:: ../../converted/locus.rst
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'^``([^`]*\([^`]*\))``$')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue