mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-04-27 16:51:06 -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
03a844556e
commit
4e26e67f3a
22 changed files with 2520 additions and 2538 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.**
|
||||
|
|
@ -1,517 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
|
||||
<head>
|
||||
|
||||
<title>Design of the MPS allocation frame protocol</title>
|
||||
|
||||
</head>
|
||||
|
||||
<body bgcolor="#FFFFFF" text="#000000" link="#000099" vlink="#660066" alink="#FF0000">
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p>
|
||||
<a href="/">Ravenbrook</a> /
|
||||
<a href="/project/">Projects</a> /
|
||||
<a href="/project/mps/">Memory Pool System</a> /
|
||||
<a href="/project/mps/master/">Master Product Sources</a> /
|
||||
<a href="/project/mps/master/design/">Design Documents</a>
|
||||
</p>
|
||||
|
||||
<p><i><a href="/project/mps/">Memory Pool System Project</a></i></p>
|
||||
|
||||
<hr />
|
||||
|
||||
</div>
|
||||
|
||||
<pre>
|
||||
DESIGN OF THE MPS ALLOCATION FRAME PROTOCOL
|
||||
design.mps.alloc-frame
|
||||
incomplete doc
|
||||
tony 1998-10-02
|
||||
|
||||
INTRODUCTION
|
||||
|
||||
<a id="intro" name="intro">.intro</a>: This document explains the design of the support for allocation frames
|
||||
in MPS.
|
||||
|
||||
<a id="readership" name="readership">.readership</a>: This document is intended for any MM developer.
|
||||
|
||||
<a id="overview" name="overview">.overview</a>: 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. <a id="overview.ambition" name="overview.ambition">.overview.ambition</a>: 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. [We plan to supersede this with a stack pool design
|
||||
at some point in the future. pekka 2000-03-09]
|
||||
|
||||
<a id="hist.0" name="hist.0">.hist.0</a>: Written by Tony 1998-10-02
|
||||
|
||||
|
||||
DEFINITIONS
|
||||
|
||||
<a id="def.alloc-frame" name="def.alloc-frame">.def.alloc-frame</a>: 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
|
||||
|
||||
<a id="purpose.stack-allocation" name="purpose.stack-allocation">.purpose.stack-allocation</a>: The allocation frame protocol is intended to support
|
||||
efficient memory management for stack allocation, i.e., the allocation of
|
||||
objects which have dynamic extent.
|
||||
|
||||
<a id="purpose.general" name="purpose.general">.purpose.general</a>: 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
|
||||
|
||||
<a id="req.stack-alloc" name="req.stack-alloc">.req.stack-alloc</a>: Provide a interface for clients to describe a stack
|
||||
allocation pattern, as an alternative to using the control stack.
|
||||
|
||||
<a id="req.efficient" name="req.efficient">.req.efficient</a>: Permit an implementation which is comparable in efficiency to
|
||||
allocating on the control stack.
|
||||
|
||||
<a id="req.ap" name="req.ap">.req.ap</a>: Support allocation via allocation points (APs).
|
||||
|
||||
<a id="req.format" name="req.format">.req.format</a>: Support the allocation of formatted objects.
|
||||
|
||||
<a id="req.scan" name="req.scan">.req.scan</a>: Ensure that objects in allocation frames can participate in garbage
|
||||
collection by being scanned.
|
||||
|
||||
<a id="req.fix" name="req.fix">.req.fix</a>: Ensure that objects in allocation frames can participate in garbage
|
||||
collection by accepting Fix requests.
|
||||
|
||||
<a id="req.condemn" name="req.condemn">.req.condemn</a>: Ensure that objects in allocation frames can participate in
|
||||
garbage collection by being condemned.
|
||||
|
||||
<a id="attr.locking" name="attr.locking">.attr.locking</a>: Minimize the synchronization cost for the creation and
|
||||
destruction of frames.
|
||||
|
||||
|
||||
Proto-requirements
|
||||
|
||||
<a id="proto-req" name="proto-req">.proto-req</a>: 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.
|
||||
|
||||
<a id="req.parallels" name="req.parallels">.req.parallels</a>: The allocation frame protocol should provide a framework for
|
||||
exploiting the parallels between stack extents, generations and "ramps".
|
||||
|
||||
<a id="req.pool-destroy" name="req.pool-destroy">.req.pool-destroy</a>: It should be possible to use allocation frames to free all
|
||||
objects in a pool without destroying the pool.
|
||||
|
||||
<a id="req.epvm" name="req.epvm">.req.epvm</a>: It should be possible to implement EPVM-style save and restore
|
||||
operations by creating and destroying allocation frames.
|
||||
|
||||
<a id="req.subst" name="req.subst">.req.subst</a>: 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.
|
||||
|
||||
<a id="req.format-extensions" name="req.format-extensions">.req.format-extensions</a>: It should be possible for stack pools to utilize the
|
||||
same format as any other pool, including debugging formats that include
|
||||
fenceposting, etc.
|
||||
|
||||
<a id="req.mis-nest" name="req.mis-nest">.req.mis-nest</a>: Should ensure "mis-nested" stacks are safe.
|
||||
|
||||
<a id="req.non-top-level" name="req.non-top-level">.req.non-top-level</a>: Should support allocation in the non-top stack extent.
|
||||
|
||||
<a id="req.copy-if-necessary" name="req.copy-if-necessary">.req.copy-if-necessary</a>: Should ensure that stack pools can support
|
||||
"copy-if-necessary" (so that low-level system code can heapify stack objects.)
|
||||
|
||||
<a id="req.preserve" name="req.preserve">.req.preserve</a>: When an object is in an allocation frame which is being
|
||||
destroyed, it should be possible to preserve that object in the parent frame.
|
||||
|
||||
<a id="req.contained" name="req.contained">.req.contained</a>: 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.
|
||||
|
||||
<a id="req.alloc-with-other" name="req.alloc-with-other">.req.alloc-with-other</a>: Should allow clients to allocate an object in the same
|
||||
frame as another object.
|
||||
|
||||
|
||||
OVERVIEW
|
||||
|
||||
<a id="frame-classes" name="frame-classes">.frame-classes</a>: 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.
|
||||
|
||||
<a id="frame-handles" name="frame-handles">.frame-handles</a>: 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.
|
||||
|
||||
<a id="lightweight-frames" name="lightweight-frames">.lightweight-frames</a>: 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
|
||||
|
||||
<a id="op.intro" name="op.intro">.op.intro</a>: 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.
|
||||
|
||||
<a id="op.obligatory" name="op.obligatory">.op.obligatory</a>: The following operations are supported on any allocation point
|
||||
which supports allocation frames:-
|
||||
|
||||
<a id="operation.push" name="operation.push">.operation.push</a>: 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.
|
||||
|
||||
<a id="operation.pop" name="operation.pop">.operation.pop</a>: 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.
|
||||
|
||||
<a id="op.optional" name="op.optional">.op.optional</a>: 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:-
|
||||
|
||||
<a id="operation.select" name="operation.select">.operation.select</a>: 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.
|
||||
|
||||
<a id="operation.select-addr" name="operation.select-addr">.operation.select-addr</a>: 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.
|
||||
|
||||
<a id="operation.in-frame" name="operation.in-frame">.operation.in-frame</a>: 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.
|
||||
|
||||
<a id="operation.set" name="operation.set">.operation.set</a>: 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 (used by all client of allocation frames)
|
||||
|
||||
<a id="type.client.frame-handle" name="type.client.frame-handle">.type.client.frame-handle</a>: Frame handles are defined as an abstract type:
|
||||
typedef struct mps_frame_s *mps_frame_t;
|
||||
|
||||
|
||||
External types (complete set)
|
||||
|
||||
<a id="type.client.frame-class" name="type.client.frame-class">.type.client.frame-class</a>: Frame classes are defined as an abstract type:
|
||||
typedef struct mps_frame_class_s *mps_frame_class_t;
|
||||
|
||||
<a id="type.client.frame-class.access" name="type.client.frame-class.access">.type.client.frame-class.access</a>: Clients access frame classes by means of
|
||||
dedicated functions for each frame class.
|
||||
|
||||
|
||||
External functions (used by all client of allocation frames)
|
||||
|
||||
<a id="fn.client.push" name="fn.client.push">.fn.client.push</a>: The following function is used by clients to invoke the
|
||||
PushFrame operation. For lightweight frames, this might not invoke the
|
||||
corresponding internal function:
|
||||
mps_res_t mps_ap_frame_push(mps_frame_t *frame_o, mps_ap_t buf);
|
||||
|
||||
<a id="fn.client.pop" name="fn.client.pop">.fn.client.pop</a>: The following function 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_pop(mps_ap_t buf, mps_frame_t frame);
|
||||
|
||||
|
||||
External functions (complete set)
|
||||
|
||||
<a id="fn.client.select" name="fn.client.select">.fn.client.select</a>: The following function is used by clients to invoke the
|
||||
SelectFrame operation:
|
||||
mps_res_t mps_ap_frame_select(mps_ap_t buf, mps_frame_t frame);
|
||||
|
||||
<a id="fn.client.select-addr" name="fn.client.select-addr">.fn.client.select-addr</a>: The following function is used by clients to invoke
|
||||
the SelectFrameOfAddr operation:
|
||||
mps_res_t mps_ap_frame_select_from_addr(mps_ap_t buf, mps_addr_t addr);
|
||||
|
||||
<a id="fn.client.in-frame" name="fn.client.in-frame">.fn.client.in-frame</a>: The following function is used by clients to invoke the
|
||||
AddrInFrame 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);
|
||||
|
||||
<a id="fn.client.set" name="fn.client.set">.fn.client.set</a>: The following function is used by clients to invoke the
|
||||
SetFrameClass operation:
|
||||
mps_res_t mps_ap_set_frame_class(mps_ap_t buf, mps_frame_class_t
|
||||
class);
|
||||
|
||||
|
||||
<a id="fn.client.stack-frame-class" name="fn.client.stack-frame-class">.fn.client.stack-frame-class</a>: The following function is used by clients to
|
||||
access the frame class used for simple stack allocation:
|
||||
mps_frame_class_t mps_alloc_frame_class_stack(void);
|
||||
|
||||
|
||||
Internal types (used by all implementations of allocation frames)
|
||||
|
||||
<a id="type.frame-handle" name="type.frame-handle">.type.frame-handle</a>: Frame handles are defined as an abstract type:
|
||||
typedef struct AllocFrameStruct *AllocFrame;
|
||||
|
||||
|
||||
Internal types (complete set)
|
||||
|
||||
<a id="type.frame-class" name="type.frame-class">.type.frame-class</a>: Frame classes are defined as an abstract type:
|
||||
typedef struct AllocFrameClassStruct *AllocFrameClass;
|
||||
|
||||
|
||||
Internal functions (used by all implementations of allocation frames)
|
||||
|
||||
|
||||
<a id="fn.push" name="fn.push">.fn.push</a>: A pool method of the following type is called (if needed) to invoke
|
||||
the PushFrame operation:
|
||||
typedef Res (*PoolFramePushMethod)(AllocFrame *frameReturn,
|
||||
Pool pool, Buffer buf);
|
||||
|
||||
<a id="fn.pop" name="fn.pop">.fn.pop</a>: A pool method of the following type is called (if needed) to invoke
|
||||
the PopFrame operation:
|
||||
typedef Res (*PoolFramePopMethod)(Pool pool, Buffer buf,
|
||||
AllocFrame frame);
|
||||
|
||||
Internal functions (complete set)
|
||||
|
||||
<a id="fn.select" name="fn.select">.fn.select</a>: A pool method of the following type is called to invoke the
|
||||
SelectFrame operation:
|
||||
typedef Res (*PoolFrameSelectMethod)(Pool pool, Buffer buf,
|
||||
AllocFrame frame);
|
||||
|
||||
<a id="fn.select-addr" name="fn.select-addr">.fn.select-addr</a>: A pool method of the following type is called to invoke the
|
||||
SelectFrameOfAddr operation:
|
||||
typedef Res (*PoolFrameSelectFromAddrMethod)(Pool pool, Buffer buf,
|
||||
Addr addr);
|
||||
|
||||
<a id="fn.in-frame" name="fn.in-frame">.fn.in-frame</a>: A pool method of the following type is called to invoke the
|
||||
AddrInFrame operation:
|
||||
typedef Res (*PoolAddrInFrameMethod)(Bool *inframeReturn,
|
||||
Pool pool, Seg seg, Addr *addrref,
|
||||
AllocFrame frame);
|
||||
|
||||
<a id="fn.set" name="fn.set">.fn.set</a>: A pool method of the following type is called to invoke the
|
||||
SetFrameClass operation:
|
||||
typedef Res (*PoolSetFrameClassMethod)(Pool pool, Buffer buf,
|
||||
AllocFrameClass class);
|
||||
|
||||
|
||||
LIGHTWEIGHT FRAMES
|
||||
|
||||
Overview
|
||||
|
||||
<a id="lw-frame.overview" name="lw-frame.overview">.lw-frame.overview</a>: 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).
|
||||
|
||||
<a id="lw-frame.model" name="lw-frame.model">.lw-frame.model</a>: 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
|
||||
|
||||
<a id="lw-frame.states" name="lw-frame.states">.lw-frame.states</a>: 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.
|
||||
|
||||
<a id="lw-frame.transitions" name="lw-frame.transitions">.lw-frame.transitions</a>: 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 (e.g. 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
|
||||
|
||||
<a id="lw-frame.state-impl" name="lw-frame.state-impl">.lw-frame.state-impl</a>: Each AP contains 3 additional fields to hold this state:
|
||||
|
||||
mps_addr_t frameptr;
|
||||
mps_bool_t enabled;
|
||||
mps_bool_t lwPopPending;
|
||||
|
||||
<a id="lw-frame.enabled" name="lw-frame.enabled">.lw-frame.enabled</a>: The enabled slot holds the following values for each state:
|
||||
Valid: TRUE
|
||||
PopPending: TRUE
|
||||
Disabled: FALSE
|
||||
|
||||
<a id="lw-frame.frameptr" name="lw-frame.frameptr">.lw-frame.frameptr</a>: 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
|
||||
|
||||
|
||||
<a id="lw-frame.state-for-gc" name="lw-frame.state-for-gc">.lw-frame.state-for-gc</a>: 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 2 additional
|
||||
AP fields in order to scan a segment which supports a lightweight allocation
|
||||
frame.
|
||||
|
||||
|
||||
Synchronization
|
||||
|
||||
<a id="lw-frame.sync" name="lw-frame.sync">.lw-frame.sync</a>: 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.
|
||||
|
||||
<a id="lw-frame.sync.pool" name="lw-frame.sync.pool">.lw-frame.sync.pool</a>: Pools are permitted to read or modify the lightweight
|
||||
frame state of an AP only in response to an operation on that AP.
|
||||
|
||||
<a id="lw-frame.sync.external" name="lw-frame.sync.external">.lw-frame.sync.external</a>: 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 iff enabled == FALSE.
|
||||
|
||||
<a id="lw-frame.sync.trip" name="lw-frame.sync.trip">.lw-frame.sync.trip</a>: When a buffer trip happens, and the trap wasn't set by MPS
|
||||
itself (i.e. 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
|
||||
|
||||
<a id="lw-frame.push" name="lw-frame.push">.lw-frame.push</a>: 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
|
||||
|
||||
|
||||
<a id="lw-frame.pop" name="lw-frame.pop">.lw-frame.pop</a>: 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
|
||||
</pre>
|
||||
|
||||
|
||||
<h2><a id="section-A" name="section-A">A. References</a></h2>
|
||||
|
||||
<!-- Template Entry
|
||||
|
||||
<table>
|
||||
|
||||
<tr valign="top">
|
||||
|
||||
<td>[<a id="ref-#REF#" name="ref-#REF#" href="#REF_URL#">#REF_NAME#</a>]</td>
|
||||
|
||||
<td>
|
||||
"#REF_TITLE#";
|
||||
#REF_AUTHOR#;
|
||||
<URL: <a href="#REF_URL#">#REF_URL#</a>>;
|
||||
#REF_DATE#.
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<h2><a id="section-B" name="section-B">B. Document History</a></h2>
|
||||
|
||||
<table>
|
||||
|
||||
<tr valign="top">
|
||||
|
||||
<td>2002-06-07</td>
|
||||
|
||||
<td><a href="mailto:rb@ravenbrook.com">RB</a></td>
|
||||
|
||||
<td>Converted from MMInfo database design document.</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
<h2><a id="section-C" name="section-C">C. Copyright and License</a></h2>
|
||||
|
||||
<p> This document is copyright © 1997-2002 <a href="http://www.ravenbrook.com/">Ravenbrook Limited</a>. All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. </p>
|
||||
|
||||
<p> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: </p>
|
||||
|
||||
<ol>
|
||||
|
||||
<li> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. </li>
|
||||
|
||||
<li> 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. </li>
|
||||
|
||||
<li> Redistributions in any form must be accompanied by information on how to obtain complete source code for the 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. </li>
|
||||
|
||||
</ol>
|
||||
|
||||
<p> <strong> 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. </strong> </p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p><code>$Id$</code></p>
|
||||
|
||||
<p>
|
||||
<a href="/">Ravenbrook</a> /
|
||||
<a href="/project/">Projects</a> /
|
||||
<a href="/project/mps/">Memory Pool System</a> /
|
||||
<a href="/project/mps/master/">Master Product Sources</a> /
|
||||
<a href="/project/mps/master/design/">Design Documents</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
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,482 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
|
||||
<head>
|
||||
|
||||
<title>Diagnostic feedback</title>
|
||||
|
||||
</head>
|
||||
|
||||
<body bgcolor="#FFFFFF" text="#000000" link="#000099" vlink="#660066" alink="#FF0000">
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p>
|
||||
<a href="/">Ravenbrook</a> /
|
||||
<a href="/project/">Projects</a> /
|
||||
<a href="/project/mps/">Memory Pool System</a> /
|
||||
<a href="/project/mps/master/">Master Product Sources</a> /
|
||||
<a href="/project/mps/master/design/">Design Documents</a>
|
||||
</p>
|
||||
|
||||
<p><i><a href="/project/mps/">Memory Pool System Project</a></i></p>
|
||||
|
||||
<hr />
|
||||
|
||||
<h1>Diagnostic feedback</h1>
|
||||
|
||||
</div>
|
||||
|
||||
<p>
|
||||
This document contains a <a href="#guide">guide</a> to MPS
|
||||
diagnostic feedback, followed by the historical
|
||||
<a href="#initial-design">initial design</a>.
|
||||
References, History, Copyright and License are
|
||||
<a href="#section-A">at the end</a>.
|
||||
</p>
|
||||
|
||||
<p> Readership: any MPS developer. Not confidential. </p>
|
||||
|
||||
<hr />
|
||||
|
||||
<h1> <a id="guide">Guide</a> </h1>
|
||||
|
||||
|
||||
<h2> Introduction </h2>
|
||||
|
||||
<p>
|
||||
Diagnostic feedback is information created by the MPS diagnostic
|
||||
system for the purpose of helping <strong>MPS programmers</strong>
|
||||
and <strong>client-code programmers</strong>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Such a piece of information is called "a diagnostic".
|
||||
(See also <a href="#parts-of-diagnostic-system">Parts of the
|
||||
MPS diagnostic system</a>, below).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
MPS diagnostic feedback code must do these things:
|
||||
</p>
|
||||
<ol>
|
||||
<li> calculate, store, and propagate data; </li>
|
||||
<li> collate, <strong>synthesise</strong>, and format it into a
|
||||
human-useful diagnostic; </li>
|
||||
<li> <strong>control</strong> (eg. filter) output of diagnostics;
|
||||
</li>
|
||||
<li> use a <strong>channel</strong> to get the diagnostic out. </li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
Note: the knowledge/code/logic for constructing the
|
||||
human-useful message is kept <em>inside normal MPS
|
||||
source code</em>. 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.
|
||||
</p>
|
||||
|
||||
|
||||
<h2> How to see some MPS diagnostic output </h2>
|
||||
|
||||
<p>
|
||||
To run the MPS and get diagnostic output from it:
|
||||
</p>
|
||||
<ul>
|
||||
<li> Use a variety with diagnostics
|
||||
compiled-in. Currently, that means
|
||||
<strong>variety.di</strong>. See config.h. </li>
|
||||
<li> Check that the diagnostics you require are generated,
|
||||
by looking in MPS source for invocations of the appropriate
|
||||
macro (eg. DIAG_SINGLEF()). </li>
|
||||
<li> Check that the diagnostics you require will be output,
|
||||
by looking at the diagnostic filter rules in diag.c. </li>
|
||||
<li> Run the MPS and client in an environment that supports the
|
||||
channel used (eg. at a command-line if using WriteF). </li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3> What is a diagnostic? </h3>
|
||||
|
||||
<p> A diagnostic has three parts:</p>
|
||||
<ul>
|
||||
<li> a trigger condition, that causes this diagnostic to be
|
||||
emitted; </li>
|
||||
<li> a text tag (eg "TraceStart") which is the name of this
|
||||
diagnostic; and </li>
|
||||
<li> a paragraph of human-useful text.</li>
|
||||
</ul>
|
||||
|
||||
<p> A diagnostic is emitted by the MPS at a certain
|
||||
point in time when a certain event happens. </p>
|
||||
|
||||
<p> 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). </p>
|
||||
|
||||
<p> 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 <object>Describe()
|
||||
methods for various relevant objects. (For example, the TraceStart
|
||||
diagnostic might call, and include the output generated by, the
|
||||
TraceDescribe() method). </p>
|
||||
|
||||
|
||||
<h3> How do I control (filter) which diagnostics I see? </h3>
|
||||
|
||||
<p> All diagnostics are emitted and then filtered according
|
||||
to the "diagnostic filter rules".</p>
|
||||
|
||||
<p> The first level of control is filtering by tag. (For example,
|
||||
only show TraceStart diagnostics). </p>
|
||||
|
||||
<p> 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).</p>
|
||||
|
||||
<p> The third level of control is filtering by line content.
|
||||
(For example, only show lines containing the word "whiteSet").</p>
|
||||
|
||||
<p> See diag.c for details.</p>
|
||||
|
||||
<p>Note: the entire filtering mechanism can be turned
|
||||
off, so that diagnostics go immediately to mps_lib_stdout,
|
||||
with no buffering or filtering See diag.c#.filter-disable.</p>
|
||||
|
||||
|
||||
|
||||
<h2> How to write a diagnostic </h2>
|
||||
|
||||
|
||||
<h3> Improve stateless Describe methods where possible </h3>
|
||||
|
||||
<p> Where possible, don't put clever code into an event-triggered
|
||||
diagnostic: put it into a stateless <object>Describe()
|
||||
method instead, and then call that method when emitting your diagnostic.</p>
|
||||
|
||||
<p> For example: </p>
|
||||
<blockquote><pre><code>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");
|
||||
...
|
||||
}
|
||||
</code></pre></blockquote>
|
||||
|
||||
<p> This is much better, because other people can use your
|
||||
human-useful output in their diagnostics, or 'live' in a
|
||||
debugger. </p>
|
||||
|
||||
|
||||
<h3> How to use DIAG_*F output macros </h3>
|
||||
|
||||
<p> For a simple diagnostic, use DIAG_SINGLEF. This begins the tag,
|
||||
puts text into the paragraph, and ends the tag immediately.</p>
|
||||
|
||||
<p> For a more complex diagnostic, the first call must be DIAG_FIRSTF,
|
||||
which begins a diag tag. </p>
|
||||
|
||||
<p> While a tag is current, you can add text to the diagnostic's
|
||||
paragraph using DIAG_MOREF, and WriteF( DIAG_STREAM, ... ).</p>
|
||||
|
||||
<p> (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().) </p>
|
||||
|
||||
<p> End the tag by calling DIAG_END.</p>
|
||||
|
||||
|
||||
<h3> Compile away in non-diag varieties; no side effects </h3>
|
||||
|
||||
<p> Wrap non-output code with the DIAG() and DIAG_DECL() macros, to
|
||||
make sure that non-diag varieties do not execute
|
||||
diagnostic-generating code.</p>
|
||||
|
||||
<p> 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 (eg. "TraceStart_diag()").</p>
|
||||
|
||||
<p> Obviously, diagnostic-generating code must have no side effects.
|
||||
</p>
|
||||
|
||||
|
||||
<h3> Choosing tags </h3>
|
||||
|
||||
<p> Tags should be valid C identifiers. Unless you know of a good
|
||||
reason why not. (Not currently checked).</p>
|
||||
|
||||
<p> There's no formal scheme for tag naming, but make it helpful and
|
||||
— informally — hierarchical, eg. TraceBegin,
|
||||
TraceStart, TraceEnd, etc. (not BeginTrace, EndTrace, ...).</p>
|
||||
|
||||
|
||||
<h3> Writing good paragraph text </h3>
|
||||
|
||||
<p> IMPORTANT: Make your diagnostics easy to understand!
|
||||
<strong>Other people</strong> 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).</p>
|
||||
|
||||
|
||||
<h3> Maintaining helpful filter rules </h3>
|
||||
|
||||
<p> If you add a 'noisy' diagnostic, add a rule to the default
|
||||
ruleset to turn it off.</p>
|
||||
|
||||
|
||||
<h2> How the MPS diagnostic system works </h2>
|
||||
|
||||
|
||||
<h3> Channels </h3>
|
||||
|
||||
<p>The recommended channel is WriteF to stdout.</p>
|
||||
|
||||
<p>
|
||||
Other possible of future channels might be:
|
||||
</p>
|
||||
<ul>
|
||||
<li> printf; </li>
|
||||
<li> a new type (yet to be defined) of mps_message; </li>
|
||||
<li> squirt them into the telemetry-log-events system; </li>
|
||||
<li> telnet. </li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Currently, only printf and WriteF are supported.
|
||||
See the DIAG_WITH_ macros in mpm.h.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can also use a debugger to call <type>Describe() methods
|
||||
directly, from within the debugger.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
|
||||
<h3 id="parts-of-diagnostic-system"> Parts of the MPS diagnostic
|
||||
system </h3>
|
||||
|
||||
<p>
|
||||
The following facilities are considered part of the MPS diagnostic
|
||||
system:
|
||||
</p>
|
||||
<ul>
|
||||
<li> the <type>Describe() methods. </li>
|
||||
<li> the DIAG* macros (DIAG, DIAG_DECL, DIAG_*F, etc);
|
||||
</li>
|
||||
<li> the STATISTIC macros (see mpm.h); </li>
|
||||
<li> the METER macros and meter subsystem; </li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3 id="related-systems"> Related Systems </h3>
|
||||
|
||||
<p>
|
||||
The MPS diagnostic system is separate from the following other MPS
|
||||
systems:
|
||||
</p>
|
||||
<ul>
|
||||
<li> The <strong>telemetry-log-events system</strong>
|
||||
(which 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 <a href="../design/telemetry/">design/telemetry</a>.
|
||||
</li>
|
||||
<li> The <strong>MPS message system</strong>
|
||||
(which 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 <a href="../design/message/">design/message</a>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<h1> <a id="initial-design">Initial Design</a> </h1>
|
||||
|
||||
|
||||
<p>
|
||||
See <a href="http://info.ravenbrook.com/mail/2007/04/13/13-07-45/0.txt">http://info.ravenbrook.com/mail/2007/04/13/13-07-45/0.txt</a>:
|
||||
"diagnostic feedback from the MPS" by RHSK.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Diverse types of diagnostic feedback:
|
||||
<a href="http://info.ravenbrook.com/mail/2007/04/18/10-58-49/0.txt">http://info.ravenbrook.com/mail/2007/04/18/10-58-49/0.txt</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
RHSK 2007-06-28
|
||||
</p>
|
||||
<p>
|
||||
It must be possible to add, modify, or remove diagnostics to affect
|
||||
diagnostic varieties, without a substantial secondary effect
|
||||
on non-diagnostic varieties. It is not a requirement that there
|
||||
be zero effect.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This means:
|
||||
</p>
|
||||
<ul>
|
||||
<li> for actual output, you must use appropriate DIAG macros
|
||||
(which compile away to nothing in non-diagnostic varieties);
|
||||
</li>
|
||||
<li> for propagating and synthesising data, use appropriate
|
||||
DIAG macros where it is reasonably clean to do so, such as for
|
||||
local variables and statements used only for diagnostic output;
|
||||
</li>
|
||||
<li> where it is clearer, and without substantial secondary effect,
|
||||
to include code (such as additional function parameters) in
|
||||
all varieties, that's okay. </li>
|
||||
</ul>
|
||||
|
||||
<p>Note: the MPS diagnostic feedback system was initially developed in
|
||||
mps/branch/2007-04-18/diag, mps/branch/2007-07-19/gcdiag, and
|
||||
mps/branch/2007-08-07/diagtag.</p>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
<h2><a id="section-A" name="section-A">A. References</a></h2>
|
||||
|
||||
<!-- Template Entry
|
||||
|
||||
<table>
|
||||
|
||||
<tr valign="top">
|
||||
|
||||
<td>[<a id="ref-#REF#" name="ref-#REF#" href="#REF_URL#">#REF_NAME#</a>]</td>
|
||||
|
||||
<td>
|
||||
"#REF_TITLE#";
|
||||
#REF_AUTHOR#;
|
||||
<URL: <a href="#REF_URL#">#REF_URL#</a>>;
|
||||
#REF_DATE#.
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<h2><a id="section-B" name="section-B">B. Document History</a></h2>
|
||||
|
||||
<table>
|
||||
|
||||
<tr valign="top">
|
||||
<td>2007-06-28</td>
|
||||
<td><a href="mailto:rhsk@ravenbrook.com">RHSK</a></td>
|
||||
<td>Create.</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td>2007-06-28</td>
|
||||
<td><a href="mailto:rhsk@ravenbrook.com">RHSK</a></td>
|
||||
<td>Telemetry-log-events system is a possible channel.</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td>2007-06-29</td>
|
||||
<td><a href="mailto:rhsk@ravenbrook.com">RHSK</a></td>
|
||||
<td>Feedback (not output), each "a diagnostic".
|
||||
Parts of the system; related systems. Link to initial design
|
||||
email.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td>2007-08-14</td>
|
||||
<td><a href="mailto:rhsk@ravenbrook.com">RHSK</a></td>
|
||||
<td>(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.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
<h2><a id="section-C" name="section-C">C. Copyright and License</a></h2>
|
||||
|
||||
<p> This document is copyright © 2007 <a href="http://www.ravenbrook.com/">Ravenbrook Limited</a>. All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. </p>
|
||||
|
||||
<p> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: </p>
|
||||
|
||||
<ol>
|
||||
|
||||
<li> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. </li>
|
||||
|
||||
<li> 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. </li>
|
||||
|
||||
<li> Redistributions in any form must be accompanied by information on how to obtain complete source code for the 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. </li>
|
||||
|
||||
</ol>
|
||||
|
||||
<p> <strong> 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. </strong> </p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p><code>$Id$</code></p>
|
||||
|
||||
<p>
|
||||
<a href="/">Ravenbrook</a> /
|
||||
<a href="/project/">Projects</a> /
|
||||
<a href="/project/mps/">Memory Pool System</a> /
|
||||
<a href="/project/mps/master/">Master Product Sources</a> /
|
||||
<a href="/project/mps/master/design/">Design Documents</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
C Style -- Formatting
|
||||
.. mode: -*- rst -*-
|
||||
|
||||
C Style -- formatting
|
||||
=====================
|
||||
|
||||
:Tag: guide.impl.c.format
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
|
||||
<tr valign="top">
|
||||
|
||||
<td> <code><a href="alloc-frame/">alloc-frame/</a></code> </td>
|
||||
<td> <code><a href="alloc-frame">alloc-frame</a></code> </td>
|
||||
|
||||
<td> Design of the MPS allocation frame protocol </td>
|
||||
|
||||
|
|
@ -145,7 +145,7 @@
|
|||
|
||||
<tr valign="top">
|
||||
|
||||
<td> <code><a href="diag/">diag/</a></code> </td>
|
||||
<td> <code><a href="diag">diag</a></code> </td>
|
||||
|
||||
<td> The design of MPS diagnostic feedback </td>
|
||||
|
||||
|
|
@ -185,7 +185,7 @@
|
|||
|
||||
<tr valign="top">
|
||||
|
||||
<td> <code><a href="interface-c/">interface-c/</a></code> </td>
|
||||
<td> <code><a href="interface-c">interface-c</a></code> </td>
|
||||
|
||||
<td> The design of the Memory Pool System interface to C </td>
|
||||
|
||||
|
|
@ -193,9 +193,9 @@
|
|||
|
||||
<tr valign="top">
|
||||
|
||||
<td> <code><a href="io/">io/</a></code> </td>
|
||||
<td> <code><a href="io">io</a></code> </td>
|
||||
|
||||
<td> The design of the MPS i/o subsystem </td>
|
||||
<td> The design of the MPS I/O subsystem </td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
|
@ -225,7 +225,7 @@
|
|||
|
||||
<tr valign="top">
|
||||
|
||||
<td> <code><a href="locus/">locus/</a></code> </td>
|
||||
<td> <code><a href="locus">locus</a></code> </td>
|
||||
|
||||
<td> The design for the locus manager </td>
|
||||
|
||||
|
|
@ -523,7 +523,7 @@
|
|||
|
||||
<tr valign="top">
|
||||
|
||||
<td> <code><a href="vman/">vman/</a></code> </td>
|
||||
<td> <code><a href="vman">vman</a></code> </td>
|
||||
|
||||
<td> ANSI fake VM </td>
|
||||
|
||||
|
|
|
|||
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.**
|
||||
|
|
@ -1,376 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
|
||||
<head>
|
||||
|
||||
<title>The design of the Memory Pool System interface to C</title>
|
||||
|
||||
</head>
|
||||
|
||||
<body bgcolor="#FFFFFF" text="#000000" link="#000099" vlink="#660066" alink="#FF0000">
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p>
|
||||
<a href="/">Ravenbrook</a> /
|
||||
<a href="/project/">Projects</a> /
|
||||
<a href="/project/mps/">Memory Pool System</a> /
|
||||
<a href="/project/mps/master/">Master Product Sources</a> /
|
||||
<a href="/project/mps/master/design/">Design Documents</a>
|
||||
</p>
|
||||
|
||||
<p><i><a href="/project/mps/">Memory Pool System Project</a></i></p>
|
||||
|
||||
<hr />
|
||||
|
||||
</div>
|
||||
|
||||
<pre>
|
||||
THE DESIGN OF THE MEMORY POOL SYSTEM INTERFACE TO C
|
||||
design.mps.interface.c
|
||||
incomplete doc
|
||||
richard 1996-07-29
|
||||
|
||||
INTRODUCTION
|
||||
|
||||
|
||||
Scope
|
||||
|
||||
<a id="scope" name="scope">.scope</a>: This document is the design for the Memory Pool System (MPS) interface
|
||||
to the C Language, impl.h.mps.
|
||||
|
||||
|
||||
Background
|
||||
|
||||
<a id="bg" name="bg">.bg</a>: See mail.richard.1996-07-24.10-57.
|
||||
|
||||
|
||||
Document History
|
||||
|
||||
<a id="hist.0" name="hist.0">.hist.0</a>: 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.
|
||||
|
||||
|
||||
ANALYSIS
|
||||
|
||||
|
||||
Goals
|
||||
|
||||
<a id="goal.c" name="goal.c">.goal.c</a>: 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. <a id="goal.cpp" name="goal.cpp">.goal.cpp</a>:
|
||||
impl.h.mps is not specifically designed to be an interface to C++, but should
|
||||
be usable from C++.
|
||||
|
||||
|
||||
Requirements
|
||||
|
||||
<a id="req" name="req">.req</a>: 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" etc.
|
||||
|
||||
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 (fmt_A, 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.
|
||||
|
||||
[.naming.global wqas presumable done in response to an unwritten requirement
|
||||
regarding the use of the name spaces in C, perhaps something like
|
||||
"<a id="req.name.iso" name="req.name.iso">.req.name.iso</a>: The interface shall not conflict in terms of naming with any
|
||||
interfaces specified by ISO C and all reasonable future versions" and
|
||||
"<a id="req.name.general" name="req.name.general">.req.name.general</a>: The interface shall use a documented and reasonably small
|
||||
portion of the namespace so that clients can interoperate easily" drj
|
||||
1998-10-01]
|
||||
|
||||
|
||||
ARCHITECTURE
|
||||
|
||||
<a id="fig.arch" name="fig.arch">.fig.arch</a>: The architecture of the MPS Interface
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
<a id="naming" name="naming">.naming</a>: The external interface names should adhere to the documented interface
|
||||
conventions; these are found in doc.mps.ref-man.if-conv(0).naming.
|
||||
(paraphrased/recreated here) <a id="naming.unixy" name="naming.unixy">.naming.unixy</a>: 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.
|
||||
<a id="naming.case" name="naming.case">.naming.case</a>: Identifiers are in lower case, except non-function-like macros,
|
||||
which are in upper case. <a id="naming.global" name="naming.global">.naming.global</a>: All publicised identifiers are
|
||||
prefixed "mps_" or "MPS_". <a id="naming.all" name="naming.all">.naming.all</a>: All identifiers defined by the MPS
|
||||
should begin "mps_" or "MPS_" or "_mps_". <a id="naming.type" name="naming.type">.naming.type</a>: Types are suffixed
|
||||
"_t". <a id="naming.struct" name="naming.struct">.naming.struct</a>: Structure types and tags are suffixed "_s".
|
||||
<a id="naming.union" name="naming.union">.naming.union</a>: Unions types and tags are suffixed "_u".
|
||||
|
||||
<a id="naming.scope" name="naming.scope">.naming.scope</a>: The naming conventions apply to all identifiers (see ISO C
|
||||
clause 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. <a id="naming.scope.labels" name="naming.scope.labels">.naming.scope.labels</a>: labels (for
|
||||
goto statements) should be rare, only in special block macros and probably not
|
||||
even then. <a id="naming.scope.other" name="naming.scope.other">.naming.scope.other</a>: 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.
|
||||
|
||||
<a id="type.gen" name="type.gen">.type.gen</a>: The interface defines memory addresses as "void *" and sizes as
|
||||
"size_t" for compatibility with standard C (in particular, with malloc etc.).
|
||||
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.
|
||||
|
||||
<a id="type.opaque" name="type.opaque">.type.opaque</a>: Opaque types are defined as pointers to structures which are
|
||||
never defined. These types are cast to the corresponding internal types in
|
||||
mpsi.c.
|
||||
|
||||
<a id="type.trans" name="type.trans">.type.trans</a>: 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().
|
||||
|
||||
<a id="type.pseudo" name="type.pseudo">.type.pseudo</a>: 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*.
|
||||
|
||||
<a id="type.enum" name="type.enum">.type.enum</a>: 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.
|
||||
|
||||
<a id="type.fun" name="type.fun">.type.fun</a>: 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. <a id="type.fun.example" name="type.fun.example">.type.fun.example</a>: 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
|
||||
then "typedef mps_addr_t (*mps_fmt_class_t)(mps_addr_t object);". See
|
||||
guide.impl.c.misc.prototype.parameters.
|
||||
|
||||
|
||||
Checking
|
||||
|
||||
<a id="check.space" name="check.space">.check.space</a>: When the space needs to be recovered from a parameter it is check
|
||||
using AVER(CHECKT(Foo, foo)) before any attempt to access FooSpace(foo).
|
||||
CHECKT (impl.h.assert) performs simple thread-safe checking of foo, so it can
|
||||
be called outside of SpaceEnter/SpaceLeave. [perhaps this should be a special
|
||||
macro. "AVER(CHECKT(" can look like the programmer made a mistake. drj
|
||||
1998-11-05]
|
||||
|
||||
<a id="check.types" name="check.types">.check.types</a>: 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.
|
||||
|
||||
This macro checks the assignment compatibility of two lvalues. The hack is
|
||||
that it uses sizeof to guarantee that the assignments have no effect.
|
||||
|
||||
#define check_lvalues(_e1, _e2) \
|
||||
(sizeof((_e1) = (_e2)), sizeof((_e2) = (_e1)), sizeof(_e1) == sizeof(_e2))
|
||||
|
||||
This macro checks that two types are 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.
|
||||
|
||||
#define check_types(_t1, _t2) check_lvalues(*((_t1 *)0), *((_t2 *)0))
|
||||
|
||||
This macro just checks that the offset and size of two fields are the same.
|
||||
|
||||
#define check_fields_approx(_s1, _f1, _s2, _f2) \
|
||||
(offsetof(_s1, _f1) == offsetof(_s2, _f2) && \
|
||||
sizeof(((_s1 *)0)->_f1) == sizeof(((_s2 *)0)->_f2))
|
||||
|
||||
This macro checks the offset, size, and compatibility of fields.
|
||||
|
||||
#define check_fields(_s1, _f1, _s2, _f2) \
|
||||
(check_lvalues(((_s1 *)0)->_f1, ((_s2 *)0)->_f2) && \
|
||||
check_fields_approx(_s1, _f1, _s2, _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
|
||||
|
||||
<a id="cons" name="cons">.cons</a>: The MPS C Interface constrains the MPS in order to provide useful memory
|
||||
management services to a C or C++ program.
|
||||
|
||||
<a id="cons.addr" name="cons.addr">.cons.addr</a>: 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.
|
||||
|
||||
<a id="pun.addr" name="pun.addr">.pun.addr</a>: 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.
|
||||
|
||||
<a id="cons.size" name="cons.size">.cons.size</a>: 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.
|
||||
|
||||
<a id="pun.size" name="pun.size">.pun.size</a>: 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.
|
||||
|
||||
<a id="cons.word" name="cons.word">.cons.word</a>: 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.
|
||||
|
||||
<a id="fmt.extend" name="fmt.extend">.fmt.extend</a>: This paragraph should be an explanation of why mps_fmt_A is so
|
||||
called. The underlying reason is future extensibility.
|
||||
|
||||
<a id="thread-safety" name="thread-safety">.thread-safety</a>: 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.
|
||||
|
||||
<a id="lock-free" name="lock-free">.lock-free</a>: 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.
|
||||
|
||||
<a id="form" name="form">.form</a>: 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.
|
||||
|
||||
</pre>
|
||||
|
||||
|
||||
<h2><a id="section-A" name="section-A">A. References</a></h2>
|
||||
|
||||
<!-- Template Entry
|
||||
|
||||
<table>
|
||||
|
||||
<tr valign="top">
|
||||
|
||||
<td>[<a id="ref-#REF#" name="ref-#REF#" href="#REF_URL#">#REF_NAME#</a>]</td>
|
||||
|
||||
<td>
|
||||
"#REF_TITLE#";
|
||||
#REF_AUTHOR#;
|
||||
<URL: <a href="#REF_URL#">#REF_URL#</a>>;
|
||||
#REF_DATE#.
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<h2><a id="section-B" name="section-B">B. Document History</a></h2>
|
||||
|
||||
<table>
|
||||
|
||||
<tr valign="top">
|
||||
|
||||
<td>2002-06-07</td>
|
||||
|
||||
<td><a href="mailto:rb@ravenbrook.com">RB</a></td>
|
||||
|
||||
<td>Converted from MMInfo database design document.</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
<h2><a id="section-C" name="section-C">C. Copyright and License</a></h2>
|
||||
|
||||
<p> This document is copyright © 1995-2002 <a href="http://www.ravenbrook.com/">Ravenbrook Limited</a>. All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. </p>
|
||||
|
||||
<p> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: </p>
|
||||
|
||||
<ol>
|
||||
|
||||
<li> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. </li>
|
||||
|
||||
<li> 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. </li>
|
||||
|
||||
<li> Redistributions in any form must be accompanied by information on how to obtain complete source code for the 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. </li>
|
||||
|
||||
</ol>
|
||||
|
||||
<p> <strong> 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. </strong> </p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p><code>$Id$</code></p>
|
||||
|
||||
<p>
|
||||
<a href="/">Ravenbrook</a> /
|
||||
<a href="/project/">Projects</a> /
|
||||
<a href="/project/mps/">Memory Pool System</a> /
|
||||
<a href="/project/mps/master/">Master Product Sources</a> /
|
||||
<a href="/project/mps/master/design/">Design Documents</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
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.**
|
||||
|
|
@ -1,494 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
|
||||
<head>
|
||||
|
||||
<title>The design of the MPS i/o subsystem</title>
|
||||
|
||||
</head>
|
||||
|
||||
<body bgcolor="#FFFFFF" text="#000000" link="#000099" vlink="#660066" alink="#FF0000">
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p>
|
||||
<a href="/">Ravenbrook</a> /
|
||||
<a href="/project/">Projects</a> /
|
||||
<a href="/project/mps/">Memory Pool System</a> /
|
||||
<a href="/project/mps/master/">Master Product Sources</a> /
|
||||
<a href="/project/mps/master/design/">Design Documents</a>
|
||||
</p>
|
||||
|
||||
<p><i><a href="/project/mps/">Memory Pool System Project</a></i></p>
|
||||
|
||||
<hr />
|
||||
|
||||
</div>
|
||||
|
||||
<pre>
|
||||
THE DESIGN OF THE MPS I/O SUBSYSTEM
|
||||
design.mps.io
|
||||
incomplete design
|
||||
richard 1996-08-30
|
||||
|
||||
INTRODUCTION
|
||||
|
||||
<a id="intro" name="intro">.intro</a>: This document is the design of the MPS I/O Subsystem, a part of the
|
||||
plinth.
|
||||
|
||||
<a id="readership" name="readership">.readership</a>: This document is intended for MPS developers.
|
||||
|
||||
|
||||
History
|
||||
|
||||
<a id="hist.1" name="hist.1">.hist.1</a>: Document created from paper notes by Richard Brooksby, 1996-08-30.
|
||||
|
||||
<a id="hist.2" name="hist.2">.hist.2</a>: 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.)
|
||||
richard 1997-06-10
|
||||
|
||||
|
||||
Background
|
||||
|
||||
<a id="bg" name="bg">.bg</a>: 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
|
||||
|
||||
<a id="purpose" name="purpose">.purpose</a>: 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.
|
||||
|
||||
<a id="purpose.measure" name="purpose.measure">.purpose.measure</a>: 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).
|
||||
|
||||
<a id="purpose.control" name="purpose.control">.purpose.control</a>: 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.
|
||||
|
||||
<a id="purpose.test" name="purpose.test">.purpose.test</a>: Test output can be used to ensure that the memory manager is
|
||||
behaving as expected in response to certain inputs.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
|
||||
|
||||
General
|
||||
|
||||
<a id="req.fun.non-hosted" name="req.fun.non-hosted">.req.fun.non-hosted</a>: The MPM must be a host-independent system.
|
||||
|
||||
<a id="req.attr.host" name="req.attr.host">.req.attr.host</a>: It should be easy for the client to set up the MPM for a
|
||||
particular host (such as a washing machine).
|
||||
|
||||
|
||||
Functional
|
||||
|
||||
<a id="req.fun.measure" name="req.fun.measure">.req.fun.measure</a>: The subsystem must allow the MPS to transmit quantitative
|
||||
measurement data to an external tool so that the system can be tuned.
|
||||
|
||||
<a id="req.fun.debug" name="req.fun.debug">.req.fun.debug</a>: The subsystem must allow the MPS to transmit qualitative
|
||||
information about its operation to an external tool so that the system can be
|
||||
debugged.
|
||||
|
||||
<a id="req.fun.control" name="req.fun.control">.req.fun.control</a>: 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.
|
||||
|
||||
<a id="req.dc.env.no-net" name="req.dc.env.no-net">.req.dc.env.no-net</a>: The subsystem should operate in environments where there is
|
||||
no networking available.
|
||||
|
||||
<a id="req.dc.env.no-fs" name="req.dc.env.no-fs">.req.dc.env.no-fs</a>: The subsystem should operate in environments where there is
|
||||
no filesystem available.
|
||||
|
||||
|
||||
ARCHITECTURE
|
||||
|
||||
<a id="arch.diagram" name="arch.diagram">.arch.diagram</a>:
|
||||
|
||||
- I/O Architecture Diagram
|
||||
|
||||
<a id="arch.int" name="arch.int">.arch.int</a>: 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.
|
||||
|
||||
<a id="arch.module" name="arch.module">.arch.module</a>: 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.
|
||||
|
||||
<a id="arch.module.example" name="arch.module.example">.arch.module.example</a>: 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.
|
||||
|
||||
<a id="arch.external" name="arch.external">.arch.external</a>: 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
|
||||
|
||||
<a id="example.telnet" name="example.telnet">.example.telnet</a>: 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.
|
||||
|
||||
|
||||
|
||||
<a id="example.file" name="example.file">.example.file</a>: 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.
|
||||
|
||||
|
||||
|
||||
<a id="example.serial" name="example.serial">.example.serial</a>: 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.
|
||||
|
||||
<a id="example.local" name="example.local">.example.local</a>: 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.
|
||||
|
||||
|
||||
- MPS I/O Configuration Diagrams
|
||||
|
||||
|
||||
|
||||
INTERFACE
|
||||
|
||||
<a id="if.msg" name="if.msg">.if.msg</a>: 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.
|
||||
|
||||
<a id="if.msg.opaque" name="if.msg.opaque">.if.msg.opaque</a>: 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.
|
||||
|
||||
<a id="if.msg.dgram" name="if.msg.dgram">.if.msg.dgram</a>: 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
|
||||
|
||||
<a id="if.state" name="if.state">.if.state</a>: 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.
|
||||
|
||||
typedef struct mps_io_s *mps_io_t;
|
||||
|
||||
|
||||
Message Types
|
||||
|
||||
<a id="if.type" name="if.type">.if.type</a>: 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
|
||||
|
||||
<a id="if.message-max" name="if.message-max">.if.message-max</a>: 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
|
||||
|
||||
<a id="if.create" name="if.create">.if.create</a>: 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
|
||||
"*mps_io_r" to a "state" value which will be passed to subsequent calls through
|
||||
the interface.
|
||||
|
||||
extern mps_res_t mps_io_create(mps_io_t *mps_io_r);
|
||||
|
||||
<a id="if.destroy" name="if.destroy">.if.destroy</a>: The MPM will call "mps_io_destroy" to tear down the I/O module,
|
||||
after which it guarantees that the state value "mps_io" will not be used
|
||||
again. The "state" parameter is the state previously returned by
|
||||
"mps_io_create" (.if.create).
|
||||
|
||||
extern void mps_io_destroy(mps_io_t mps_io);
|
||||
|
||||
|
||||
Message Send and Receive
|
||||
|
||||
<a id="if.send" name="if.send">.if.send</a>: 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.
|
||||
[Should there be a timeout parameter? What are the timing constraints?
|
||||
mps_io_send shouldn't block.]
|
||||
|
||||
extern mps_res_t mps_io_send(mps_io_t state,
|
||||
mps_io_type_t type,
|
||||
void *message,
|
||||
size_t size);
|
||||
|
||||
<a id="if.receive" name="if.receive">.if.receive</a>: 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.
|
||||
(Should we be able to receive truncated messages? How can this be done neatly?)
|
||||
|
||||
extern mps_res_t mps_io_receive(mps_io_t state,
|
||||
void **buffer_o,
|
||||
size_t *size_o);
|
||||
|
||||
|
||||
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"
|
||||
|
||||
</pre>
|
||||
|
||||
|
||||
<h2><a id="section-A" name="section-A">A. References</a></h2>
|
||||
|
||||
<!-- Template Entry
|
||||
|
||||
<table>
|
||||
|
||||
<tr valign="top">
|
||||
|
||||
<td>[<a id="ref-#REF#" name="ref-#REF#" href="#REF_URL#">#REF_NAME#</a>]</td>
|
||||
|
||||
<td>
|
||||
"#REF_TITLE#";
|
||||
#REF_AUTHOR#;
|
||||
<URL: <a href="#REF_URL#">#REF_URL#</a>>;
|
||||
#REF_DATE#.
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<h2><a id="section-B" name="section-B">B. Document History</a></h2>
|
||||
|
||||
<table>
|
||||
|
||||
<tr valign="top">
|
||||
|
||||
<td>2002-06-07</td>
|
||||
|
||||
<td><a href="mailto:rb@ravenbrook.com">RB</a></td>
|
||||
|
||||
<td>Converted from MMInfo database design document.</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
<h2><a id="section-C" name="section-C">C. Copyright and License</a></h2>
|
||||
|
||||
<p> This document is copyright © 1995-2002 <a href="http://www.ravenbrook.com/">Ravenbrook Limited</a>. All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. </p>
|
||||
|
||||
<p> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: </p>
|
||||
|
||||
<ol>
|
||||
|
||||
<li> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. </li>
|
||||
|
||||
<li> 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. </li>
|
||||
|
||||
<li> Redistributions in any form must be accompanied by information on how to obtain complete source code for the 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. </li>
|
||||
|
||||
</ol>
|
||||
|
||||
<p> <strong> 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. </strong> </p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p><code>$Id$</code></p>
|
||||
|
||||
<p>
|
||||
<a href="/">Ravenbrook</a> /
|
||||
<a href="/project/">Projects</a> /
|
||||
<a href="/project/mps/">Memory Pool System</a> /
|
||||
<a href="/project/mps/master/">Master Product Sources</a> /
|
||||
<a href="/project/mps/master/design/">Design Documents</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
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.**
|
||||
|
|
@ -1,645 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
|
||||
<head>
|
||||
|
||||
<title>The MPS Locus Manager</title>
|
||||
|
||||
</head>
|
||||
|
||||
<body bgcolor="#FFFFFF" text="#000000" link="#000099" vlink="#660066" alink="#FF0000">
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p>
|
||||
<a href="/">Ravenbrook</a> /
|
||||
<a href="/project/">Projects</a> /
|
||||
<a href="/project/mps/">Memory Pool System</a> /
|
||||
<a href="/project/mps/master/">Master Product Sources</a> /
|
||||
<a href="/project/mps/master/design/">Design Documents</a>
|
||||
</p>
|
||||
|
||||
<p><i><a href="/project/mps/">Memory Pool System Project</a></i></p>
|
||||
|
||||
<hr />
|
||||
|
||||
<h1>The MPS Locus Manager</h1>
|
||||
|
||||
</div>
|
||||
|
||||
<p>This document contains a <a href="#guide">guide</a> to the MPS locus manager, followed by the historical <a href="#initial-design">initial design</a>. References, History, Copyright and License are <a href="#section-A">at the end</a>.</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<h1> <a id="guide">Guide</a> </h1>
|
||||
|
||||
<p> Readership: any MPS developer. Not confidential. </p>
|
||||
|
||||
|
||||
<p>The MPS manages three main resources:</p>
|
||||
<ul>
|
||||
<li>storage;</li>
|
||||
<li>address-space;</li>
|
||||
<li>time.</li>
|
||||
</ul>
|
||||
|
||||
<h2> Management of arena address-space </h2>
|
||||
|
||||
<p>The locus manager manages address-space at the arena level.</p>
|
||||
|
||||
<p>[Tucker was right: <a href="http://info.ravenbrook.com/project/mps/mail/1998/11/02/14-25/0.txt">mail.ptw.1998-11-02.14-25</a>. RHSK 2007-04-24].</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>Preferences are expressed by the SegPref argument to SegAlloc().</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
|
||||
<h2> Why is it important to manage address-space? </h2>
|
||||
|
||||
|
||||
<h3> Trace differentiability </h3>
|
||||
|
||||
<p>Carefully chosen addresses are used by reference tracing systems (ie. automatic pools), to:</p>
|
||||
<ul>
|
||||
<li> categorise objects into clumps; </li>
|
||||
<li> summarise and cheaply find references between clumps. </li>
|
||||
</ul>
|
||||
|
||||
<p>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).</p>
|
||||
|
||||
<p>This is what the MPS zone mechanism is all about.</p>
|
||||
|
||||
<p>The locus manager manages the mapping from clumps to zones.</p>
|
||||
|
||||
<p>To specify a clump, pools use the "SegPrefGen" tag to SegPrefExpress. [The name is misleading: generations are only one sort of clump. RHSK 2007-04-24]</p>
|
||||
|
||||
|
||||
<h3> Prevent address-space fragmentation (within arena) </h3>
|
||||
|
||||
<p>Address-space is not infinite.</p>
|
||||
|
||||
<p>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).</p>
|
||||
|
||||
<p>Even with the VM arena class, typical storage sizes (in the year 2007) can make 32-bit address-space constrained: the problem may have Order(4GB) size, which leaves little 'spare' address-space.</p>
|
||||
|
||||
<p>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 3rd-party graphics library, image library, etc.</p>
|
||||
|
||||
<p>Address-space fragmentation incurs cost when:</p>
|
||||
<ul>
|
||||
<li> 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; </li>
|
||||
<li> possible operating-system costs in maintaining a fragmented mapping? </li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3> Prevent storage fragmentation (within tracts and segments) </h3>
|
||||
|
||||
<p>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. </p>
|
||||
|
||||
<p>Non-moving pools manage this fragmentation with placement strategies that use:</p>
|
||||
<ul>
|
||||
<li> co-located death (in space and time); </li>
|
||||
<li> segment merging and splitting. </li>
|
||||
</ul>
|
||||
|
||||
<p>These pool-level strategies always care about contiguity of object storage. They also often care about the <em>ordering</em> 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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
|
||||
<h2> Discovering the layout </h2>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<h1> <a id="initial-design">Initial Design</a> </h1>
|
||||
|
||||
<pre>
|
||||
THE DESIGN FOR THE LOCUS MANAGER
|
||||
design.mps.locus
|
||||
incomplete design
|
||||
gavinm 1998-02-27
|
||||
|
||||
INTRODUCTION
|
||||
|
||||
<a id="intro" name="intro">.intro</a>: 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.
|
||||
|
||||
<a id="source" name="source">.source</a>: 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).
|
||||
|
||||
|
||||
Document History
|
||||
|
||||
<a id="hist.0" name="hist.0">.hist.0</a>: Originally written as part of change.dylan.box-turtle.170569. Much
|
||||
developed since. gavinm 1998-02-27
|
||||
|
||||
<a id="hist.1" name="hist.1">.hist.1</a>: Pekka wrote the real requirements after some discussion. pekka
|
||||
1998-10-28
|
||||
|
||||
<a id="hist.2" name="hist.2">.hist.2</a>: Pekka deleted Gavin's design and wrote a new one. pekka 1998-12-15
|
||||
|
||||
|
||||
DEFINITIONS
|
||||
|
||||
<a id="note.cohort" name="note.cohort">.note.cohort</a>: 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.
|
||||
|
||||
<a id="def.trace.differentiability" name="def.trace.differentiability">.def.trace.differentiability</a>: 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
|
||||
|
||||
<a id="req.cohort" name="req.cohort">.req.cohort</a>: 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.
|
||||
|
||||
<a id="req.counter.objects" name="req.counter.objects">.req.counter.objects</a>: As a counter-requirement, pools are expected to manage
|
||||
objects. Objects the size of a tract allocation request (segment-sized) are
|
||||
exceptional. Critical. <a id="req.counter.objects.just" name="req.counter.objects.just">.req.counter.objects.just</a>: 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.
|
||||
|
||||
<a id="req.contiguity" name="req.contiguity">.req.contiguity</a>: 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).
|
||||
<a id="req.contiguity.just" name="req.contiguity.just">.req.contiguity.just</a>: TSBA.
|
||||
|
||||
<a id="req.contiguity.specific" name="req.contiguity.specific">.req.contiguity.specific</a>: 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.
|
||||
[IWBN to have one for "next to the largest free block".]
|
||||
|
||||
<a id="req.differentiable" name="req.differentiable">.req.differentiable</a>: 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.
|
||||
|
||||
<a id="req.differentiable.integer" name="req.differentiable.integer">.req.differentiable.integer</a>: It must be possible to place collectable
|
||||
allocations so that they are trace-differentiable from small integers.
|
||||
Essential.
|
||||
|
||||
<a id="req.disjoint" name="req.disjoint">.req.disjoint</a>: Must support the disjointness of pages that have different VM
|
||||
properties (such as mutable/immutable, read-only/read-write, and different
|
||||
lifetimes). Optional. [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 1998-10-28]
|
||||
|
||||
<a id="req.low-memory" name="req.low-memory">.req.low-memory</a>: The architecture of the locus manager must not prevent the
|
||||
design of efficient applications that often use all available memory.
|
||||
Critical. <a id="req.low-memory.expl" name="req.low-memory.expl">.req.low-memory.expl</a>: 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.
|
||||
|
||||
<a id="req.address" name="req.address">.req.address</a>: Must conserve address space in VM arenas to a reasonable extent.
|
||||
Critical.
|
||||
|
||||
<a id="req.inter-pool" name="req.inter-pool">.req.inter-pool</a>: Must support the association of sets of tracts in different
|
||||
pools into one cohort. Nice.
|
||||
|
||||
<a id="req.ep-style" name="req.ep-style">.req.ep-style</a>: 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). <a id="req.ep-style.just" name="req.ep-style.just">.req.ep-style.just</a>: We
|
||||
cannot risk disrupting a policy with well-known properties when this technology
|
||||
is introduced.
|
||||
|
||||
<a id="req.attributes" name="req.attributes">.req.attributes</a>: 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, [more in the future]. Optional. [It's a given that the
|
||||
cohorts must then have these attributes, within the limits set in the contract
|
||||
of the appropriate interface.] <a id="req.attributes.action" name="req.attributes.action">.req.attributes.action</a>: The locus manager
|
||||
should use the attributes to guide its placement decisions. Nice.
|
||||
|
||||
<a id="req.blacklisting" name="req.blacklisting">.req.blacklisting</a>: 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. [How to do blacklist breaking for ambiguous refs?]
|
||||
Optional.
|
||||
|
||||
<a id="req.hysteresis" name="req.hysteresis">.req.hysteresis</a>: 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
|
||||
|
||||
<a id="anal.sw" name="anal.sw">.anal.sw</a>: Almost any placement policy would be an improvement on the current SW
|
||||
one.
|
||||
|
||||
<a id="anal.cause-and-effect" name="anal.cause-and-effect">.anal.cause-and-effect</a>: The locus manager doesn't usually need to know _why_
|
||||
things need to be differentiable, disjoint, contiguous, etc. 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.
|
||||
|
||||
<a id="anal.stable" name="anal.stable">.anal.stable</a>: 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).
|
||||
|
||||
<a id="anal.fragmentation" name="anal.fragmentation">.anal.fragmentation</a>: 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. <a id="anal.deathtime" name="anal.deathtime">.anal.deathtime</a>: 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).
|
||||
|
||||
<a id="anal.asymmetrical" name="anal.asymmetrical">.anal.asymmetrical</a>: 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
|
||||
|
||||
<a id="interface.locus" name="interface.locus">.interface.locus</a>: 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).
|
||||
|
||||
<a id="interface.locus.pool" name="interface.locus.pool">.interface.locus.pool</a>: 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].
|
||||
|
||||
<a id="interface.detail" name="interface.detail">.interface.detail</a>: This describes interface in overview; for details, see
|
||||
implementation section and code, or user doc.
|
||||
|
||||
|
||||
Loci
|
||||
|
||||
<a id="function.create" name="function.create">.function.create</a>: A function to create a locus:
|
||||
Res LocusCreate(Locus *locusReturn, LocusAttrs attrs, ZoneGroup zg,
|
||||
LocusAllocDesc adesc)
|
||||
where adesc contains the information about the allocation sequences in the
|
||||
locus, zg is used for zone differentiability, and attrs encodes the following:
|
||||
<a id="locus.contiguity" name="locus.contiguity">.locus.contiguity</a>: 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).
|
||||
<a id="locus.blacklist" name="locus.blacklist">.locus.blacklist</a>: Allocations in the locus will avoid blacklisted pages (for
|
||||
collectable segments).
|
||||
<a id="locus.zero" name="locus.zero">.locus.zero</a>: Allocations in the locus are zero-filled.
|
||||
[Other attributes will be added, I'm sure.]
|
||||
|
||||
<a id="interface.zone-group" name="interface.zone-group">.interface.zone-group</a>: 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). [I propose no mechanism for managing zone groups at this
|
||||
time, since it's only used internally for one purpose. pekka 2000-01-17]
|
||||
|
||||
<a id="interface.size" name="interface.size">.interface.size</a>: An allocation descriptor (LocusAllocDesc) contains various
|
||||
descriptions of how the locus will develop over time (inconsistent
|
||||
specifications are forbidden, of course):
|
||||
<a id="interface.size.typical-alloc" name="interface.size.typical-alloc">.interface.size.typical-alloc</a>: Size of a typical allocation in this locus, in
|
||||
bytes. This will mainly affect the grouping of non-contiguous loci.
|
||||
<a id="interface.size.large-alloc" name="interface.size.large-alloc">.interface.size.large-alloc</a>: 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.
|
||||
<a id="interface.size.direction" name="interface.size.direction">.interface.size.direction</a>: Direction of growth: up/down/none. Only useful if
|
||||
the locus is contiguous.
|
||||
<a id="interface.size.lifetime" name="interface.size.lifetime">.interface.size.lifetime</a>: Some measure of the lifetime of tracts (not
|
||||
objects) in the cohort. [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 2000-01-17]
|
||||
<a id="interface.size.deathtime" name="interface.size.deathtime">.interface.size.deathtime</a>: Some measure of the deathtime of tracts (not
|
||||
objects) in the cohort. [Ditto. pekka 2000-01-17]
|
||||
|
||||
<a id="function.init" name="function.init">.function.init</a>: LocusInit is like LocusCreate, but without the allocation.
|
||||
This is the usual i/f, since most loci are embedded in a pool or something.
|
||||
|
||||
<a id="function.alloc" name="function.alloc">.function.alloc</a>: ArenaAlloc to take a locus arg. 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 realloc functionality).
|
||||
|
||||
<a id="function.set-total" name="function.set-total">.function.set-total</a>: A function to tell the arena the expected number of
|
||||
(non-miscible client) loci, and of zone groups:
|
||||
ArenaSetTotalLoci(Arena arena, Size nLoci, Size nZoneGroups)
|
||||
|
||||
|
||||
Peaks
|
||||
|
||||
<a id="function.peak.create" name="function.peak.create">.function.peak.create</a>: A function to create a peak:
|
||||
mps_res_t mps_peak_create(mps_peak_t*, mps_arena_t)
|
||||
A newly-created peak is open, and will not be used to guide the strategy of the
|
||||
locus manager.
|
||||
|
||||
<a id="function.peak.add" name="function.peak.add">.function.peak.add</a>: A function to add a description of the state of one pool
|
||||
into the peak:
|
||||
mps_res_t mps_peak_describe_pool(mps_peak_t, mps_pool_t, mps_size_desc_t)
|
||||
Calling this function again for the same peak and pool instance will replace
|
||||
the earlier description. <a id="function.peak.add.size" name="function.peak.add.size">.function.peak.add.size</a>: The size descriptor contains
|
||||
a total size in bytes or % of arena size [@@@@is this right?].
|
||||
<a id="function.peak.add.remove" name="function.peak.add.remove">.function.peak.add.remove</a>: 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.
|
||||
|
||||
<a id="function.peak.close" name="function.peak.close">.function.peak.close</a>: 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:
|
||||
mps_res_t mps_peak_close(mps_peak_t)
|
||||
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.
|
||||
<a id="function.peak.close.after" name="function.peak.close.after">.function.peak.close.after</a>: 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.
|
||||
|
||||
<a id="function.peak.destroy" name="function.peak.destroy">.function.peak.destroy</a>: A function to destroy a peak:
|
||||
void mps_peak_destroy(mps_peak_t)
|
||||
|
||||
<a id="interface.ep-style" name="interface.ep-style">.interface.ep-style</a>: 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). [Not sure this is good enough, but we'll try
|
||||
it first. pekka 2000-01-17]
|
||||
|
||||
|
||||
ARCHITECTURE
|
||||
|
||||
Data Objects
|
||||
|
||||
<a id="arch.locus" name="arch.locus">.arch.locus</a>: To represent the cohorts, we have locus objects. Usually a locus
|
||||
is embedded in a pool instance, but generations are separate loci.
|
||||
|
||||
<a id="arch.locus.attr" name="arch.locus.attr">.arch.locus.attr</a>: contiguity, blacklist, zg, current region, @@@@
|
||||
|
||||
<a id="arch.locus.attr.exceptional" name="arch.locus.attr.exceptional">.arch.locus.attr.exceptional</a>: The client can define a typical large allocation
|
||||
for the locus. Requests substantially larger than that are deemed exceptional.
|
||||
|
||||
<a id="arch.zone-group" name="arch.zone-group">.arch.zone-group</a>: 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 @@@@.
|
||||
|
||||
<a id="arch.page-table" name="arch.page-table">.arch.page-table</a>: A page table is maintained by the arena, as usual to track
|
||||
association between tracts, pools and segments, and mapping status for VM
|
||||
arenas.
|
||||
|
||||
<a id="arch.region" name="arch.region">.arch.region</a>: 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.
|
||||
|
||||
<a id="arch.region.assoc" name="arch.region.assoc">.arch.region.assoc</a>: 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". <a id="arch.locus.assoc" name="arch.locus.assoc">.arch.locus.assoc</a>: 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.
|
||||
|
||||
<a id="arch.region.more" name="arch.region.more">.arch.region.more</a>: 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).
|
||||
|
||||
<a id="arch.chunk" name="arch.chunk">.arch.chunk</a>: 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
|
||||
|
||||
<a id="arch.strategy.delay" name="arch.strategy.delay">.arch.strategy.delay</a>: The general strategy is to delay placement decisions
|
||||
until they have to be made, but no later.
|
||||
|
||||
<a id="arch.strategy.delay.until" name="arch.strategy.delay.until">.arch.strategy.delay.until</a>: 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).
|
||||
|
||||
<a id="arch.strategy.normal" name="arch.strategy.normal">.arch.strategy.normal</a>: 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.
|
||||
|
||||
<a id="arch.strategy.normal.limit" name="arch.strategy.normal.limit">.arch.strategy.normal.limit</a>: 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.
|
||||
|
||||
<a id="arch.strategy.create" name="arch.strategy.create">.arch.strategy.create</a>: 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
|
||||
|
||||
<a id="arch.alloc" name="arch.alloc">.arch.alloc</a>: 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).
|
||||
|
||||
<a id="arch.alloc.same" name="arch.alloc.same">.arch.alloc.same</a>: 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. <a id="arch.alloc.same.hole" name="arch.alloc.same.hole">.arch.alloc.same.hole</a>:
|
||||
ATM, 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.
|
||||
|
||||
<a id="arch.alloc.extend" name="arch.alloc.extend">.arch.alloc.extend</a>: 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.) <a id="arch.alloc.extend.here" name="arch.alloc.extend.here">.arch.alloc.extend.here</a>: 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].
|
||||
|
||||
<a id="arch.alloc.other" name="arch.alloc.other">.arch.alloc.other</a>: 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?]
|
||||
|
||||
<a id="arch.alloc.recompute" name="arch.alloc.recompute">.arch.alloc.recompute</a>: 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).
|
||||
|
||||
<a id="arch.alloc.current" name="arch.alloc.current">.arch.alloc.current</a>: 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 @@@@).
|
||||
|
||||
<a id="arch.significant" name="arch.significant">.arch.significant</a>: 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
|
||||
|
||||
<a id="arch.free" name="arch.free">.arch.free</a>: 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.
|
||||
<a id="arch.free.remove" name="arch.free.remove">.arch.free.remove</a>: If a region becomes entirely empty, it is deleted (and the
|
||||
neighbors limits might be adjusted [quite tricky to get right, this]).
|
||||
|
||||
|
||||
Region Placement Recomputation
|
||||
|
||||
<a id="arch.gap" name="arch.gap">.arch.gap</a>: 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. [Edge
|
||||
determination is actually a worthwhile direction to explore.]
|
||||
|
||||
<a id="arch.reach" name="arch.reach">.arch.reach</a>: 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.
|
||||
|
||||
<a id="arch.placement.object" name="arch.placement.object">.arch.placement.object</a>: 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.
|
||||
|
||||
<a id="arch.placement.hypothetical" name="arch.placement.hypothetical">.arch.placement.hypothetical</a>: 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.
|
||||
|
||||
<a id="arch.placement.interesting" name="arch.placement.interesting">.arch.placement.interesting</a>: The computation will only consider new regions of
|
||||
loci that are deemed interesting, i.e., far from their peak state. This will
|
||||
reduce the computational burden and avoid jittering near a peak.
|
||||
|
||||
[details missing]
|
||||
|
||||
|
||||
IMPLEMENTATION
|
||||
|
||||
[missing]
|
||||
|
||||
|
||||
NOTES
|
||||
|
||||
<a id="idea.change" name="idea.change">.idea.change</a>: Even after the first segment, be prepared to change your mind, if
|
||||
by the second segment a lot of new loci have been created.
|
||||
|
||||
<a id="distance" name="distance">.distance</a>: 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).
|
||||
|
||||
<a id="clear-pool" name="clear-pool">.clear-pool</a>: Need to have a function to deallocate all objects in a pool, so
|
||||
that PoolDestroy won't have to be used for that purpose.
|
||||
|
||||
</pre>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2><a id="section-A" name="section-A">A. References</a></h2>
|
||||
|
||||
<!-- Template Entry
|
||||
|
||||
<table>
|
||||
|
||||
<tr valign="top">
|
||||
|
||||
<td>[<a id="ref-#REF#" name="ref-#REF#" href="#REF_URL#">#REF_NAME#</a>]</td>
|
||||
|
||||
<td>
|
||||
"#REF_TITLE#";
|
||||
#REF_AUTHOR#;
|
||||
<URL: <a href="#REF_URL#">#REF_URL#</a>>;
|
||||
#REF_DATE#.
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<h2><a id="section-B" name="section-B">B. Document History</a></h2>
|
||||
|
||||
<table>
|
||||
|
||||
<tr valign="top">
|
||||
<td>2002-06-07</td>
|
||||
<td><a href="mailto:rb@ravenbrook.com">RB</a></td>
|
||||
<td>Converted from MMInfo database design document.</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td>2007-04-24</td>
|
||||
<td><a href="mailto:rhsk@ravenbrook.com">RHSK</a></td>
|
||||
<td>add Guide: Manage arena address-space, why, discover layout.</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
<h2><a id="section-C" name="section-C">C. Copyright and License</a></h2>
|
||||
|
||||
<p> This document is copyright © 1995-2002, 2007 <a href="http://www.ravenbrook.com/">Ravenbrook Limited</a>. All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options. </p>
|
||||
|
||||
<p> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: </p>
|
||||
|
||||
<ol>
|
||||
|
||||
<li> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. </li>
|
||||
|
||||
<li> 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. </li>
|
||||
|
||||
<li> Redistributions in any form must be accompanied by information on how to obtain complete source code for the 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. </li>
|
||||
|
||||
</ol>
|
||||
|
||||
<p> <strong> 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. </strong> </p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p><code>$Id$</code></p>
|
||||
|
||||
<p>
|
||||
<a href="/">Ravenbrook</a> /
|
||||
<a href="/project/">Projects</a> /
|
||||
<a href="/project/mps/">Memory Pool System</a> /
|
||||
<a href="/project/mps/master/">Master Product Sources</a> /
|
||||
<a href="/project/mps/master/design/">Design Documents</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -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