1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-04-27 16:51:06 -07:00

Improving documentation in response to review by gdr.

See <https://info.ravenbrook.com/mail/2016/04/14/10-02-00/0/> and <https://info.ravenbrook.com/mail/2016/04/14/11-21-48/0/>.

Copied from Perforce
 Change: 191229
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Richard Brooksby 2016-04-19 13:28:00 +01:00
parent 08060a3b05
commit f21d24ee6c
3 changed files with 251 additions and 203 deletions

View file

@ -167,8 +167,8 @@ extern unsigned CheckLevel;
#define AVER_CRITICAL DISCARD
#define AVERT_CRITICAL(type, val) DISCARD(ASSERT_ISTYPE(type, val))
#define AVERC_CRITICAL(class, val) DISCARD(ASSERT_ISCLASS(class, val))
#define AVERP_CRITICAL(cond, dflt) (dflt)
#define AVERPC_CRITICAL(cond, condstring, dflt) (dflt)
#define AVERP_CRITICAL(cond, dflt) (DISCARD_EXP(cond), dflt)
#define AVERPC_CRITICAL(cond, condstring, dflt) (DISCARD_EXP(cond), dflt)
#endif

View file

@ -14,11 +14,48 @@
#include "classdef.h"
/* CLASS_* -- identifier derivation macros.
/* Identifier derivation macros.
*
* These turn the base identifier of a class (e.g. "Inst") into other
* identifiers (e.g. "InstClassStruct"). These are not intended to be
* used outside of this file.
* used outside of this file. These macros implement
* design.mps.protocol.overview.naming and
* design.mps.impl.derived-names.
*
* INST_TYPE derives the type of an instance of the class,
* e.g. "Land", which will be a pointer to an INST_STRUCT.
*
* INST_STRUCT derives the type of a structure of an instance,
* e.g. "LandStruct".
*
* INST_CHECK derives the name of the checking function for the
* instance, e.g. "LandCheck".
*
* CLASS_TYPE derives the type of the class, e.g. "LandClass", which
* will be a pointer to a CLASS_STRUCT.
*
* CLASS_STRUCT derives the type of the structure of the class,
* e.g. "LandClassStruct".
*
* CLASS_ENSURE derives the name of the ensure function that returns
* the canonical class object, e.g. "LandClassGet".
*
* CLASS_INIT derives the name of the init function that initializes a
* CLASS_STRUCT, e.g. "LandClassInit".
*
* CLASS_CHECK derives the name of the checking function for the
* class, e.g. "LandClassCheck".
*
* CLASS_GUARDIAN derives the name of a boolean that indicates whether
* the canonical class object has been initialized yet,
* e.g. "ClassGuardianLand".
*
* CLASS_STATIC derives the name of a static global variable that
* contains the canonical class object, e.g. "ClassStaticLand".
*
* KIND_CLASS derives the class name of a kind, which is used in
* contexts like CLASS_TYPE(KIND_CLASS(kind)), so that the kind "Land"
* is implemented by the canonical "LandClassClass".
*/
#define INST_TYPE(ident) ident
@ -29,7 +66,6 @@
#define CLASS_ENSURE(ident) ident ## ClassGet
#define CLASS_INIT(ident) ident ## ClassInit
#define CLASS_CHECK(ident) ident ## ClassCheck
#define CLASS_SUPER(ident) ident ## SuperClassGet
#define CLASS_GUARDIAN(ident) ClassGuardian ## ident
#define CLASS_STATIC(ident) ClassStatic ## ident
#define KIND_CLASS(ident) ident ## Class
@ -38,7 +74,8 @@
/* DECLARE_CLASS -- declare the existence of a protocol class
*
* Declares a prototype for the class ensure function, which ensures
* that the class is initialized once and return it.
* that the class is initialized once and return it. See
* design.mps.protocol.if.declare-class.
*/
#define DECLARE_CLASS(kind, ident) \
@ -46,7 +83,14 @@
extern void CLASS_INIT(ident)(CLASS_TYPE(kind) var)
/* DEFINE_CLASS -- define a protocol class */
/* DEFINE_CLASS -- define a protocol class
*
* Defines the static storage and functions for the canonical class
* object for a class. Takes care to avoid initializing the class
* twice, even when called asynchronously from multiple threads, since
* this code can be reached without first entering an arena. See
* design.mps.protocol.if.define-class.
*/
#define DEFINE_CLASS(kind, ident, var) \
DECLARE_CLASS(kind, ident); \
@ -74,7 +118,11 @@
void CLASS_INIT(ident)(CLASS_TYPE(kind) var)
/* CLASS -- expression for getting a class */
/* CLASS -- expression for getting a class
*
* Use this to get a class, rather than calling anything defined by
* DEFINE_CLASS directly. See design.mps.protocol.if.class.
*/
#define CLASS(ident) (CLASS_ENSURE(ident)())
@ -83,7 +131,7 @@
*
* This defines enum constants like ClassIdLand with a unique small
* number for each class -- essentially the row number in the class
* table.
* table. Used to implement design.mps.protocol.impl.subclass.
*/
#define CLASS_ID_ENUM(prefix, ident, kind, super) prefix ## ident,
@ -97,10 +145,12 @@ typedef enum ClassIdEnum {
/* ClassLevelEnum -- depth of class in hierarchy
*
* This defines enum constants like ClassLevelLand equal to the
* distance from the root of the class hierarchy.
* distance from the root of the class hierarchy. Used to implement
* design.mps.protocol.impl.subclass.
*/
#define CLASS_LEVEL_ENUM(prefix, ident, kind, super) prefix ## ident = prefix ## super + 1,
#define CLASS_LEVEL_ENUM(prefix, ident, kind, super) \
prefix ## ident = prefix ## super + 1,
typedef enum ClassLevelEnum {
ClassLevelNoSuper = -1, /* so that root classes (e.g. Inst) get level zero */
CLASSES(CLASS_LEVEL_ENUM, ClassLevel)
@ -111,8 +161,8 @@ typedef enum ClassLevelEnum {
/* INHERIT_CLASS -- inheriting from a superclass
*
* This macro is used at the start of a class definition to inherit
* the superclass and override the fields essential to the
* workings of the protocol.
* the superclass and override the fields essential to the workings of
* the protocol. See design.mps.protocol.if.inheritance.
*/
#define INHERIT_CLASS(this, _class, super) \
@ -127,12 +177,11 @@ typedef enum ClassLevelEnum {
END
/* Inst -- the instance structure for support of the protocol
/* Inst -- the base class of the hierarchy
*
* An InstStruct named instStruct must be the first field of any
* instance structure using the protocol, because the protocol uses
* casting between structures with common prefixes to implement
* polymorphism.
* instance structure using the protocol
* (design.mps.protocol.overview.prefix).
*/
typedef struct InstStruct *Inst;
@ -143,15 +192,12 @@ typedef struct InstStruct {
/* Do not add permanent fields here. Introduce a subclass. */
} InstStruct;
/* InstClass -- the class containing the support for the protocol */
typedef const char *ClassName;
typedef unsigned char ClassId;
typedef unsigned char ClassLevel;
#define ClassDEPTH 8 /* maximum depth of class hierarchy */
#define InstClassSig ((Sig)0x519B60C7) /* SIGnature PROtocol CLass */
#define InstClassSig ((Sig)0x519B1452) /* SIGnature Protocol INST */
typedef struct InstClassStruct {
InstStruct instStruct; /* classes are instances of kinds */
@ -183,12 +229,9 @@ extern void ClassRegister(InstClass class);
/* IsSubclass, IsA -- fast subclass test
*
* The InstClassStruct is arranged to make this test fast and simple,
* so that it can be used as a consistency check in the MPS. Each
* class has an array of the ids of its superclasses, indexed by the
* level in the hierarchy of the class. The level and id are
* statically known, so they can be tested by accessing just one
* class.
* The InstClassStruct is arranged to make these tests fast and
* simple, so that it can be used as a consistency check in the MPS.
* See design.mps.protocol.impl.subclass.
*/
#define IsSubclass(sub, super) \
@ -205,14 +248,17 @@ extern void ClassRegister(InstClass class);
/* CouldBeA, MustBeA -- coerce instances
*
* CouldBeA converts an instance to another class without checking.
* It is intended to be equivalent to the C++ "static_cast", although
* since this is C there is no actual static checking, so in fact it's
* more like "reinterpret_cast".
* CouldBeA converts an instance to another class without checking,
* like C++ ``static_cast``. See design.mps.protocol.if.could-be-a.
*
* MustBeA converts an instance to another class, but checks that the
* object is a subclass, causing an assertion if not (depending on
* build variety). It is like C++ "dynamic_cast" with an assert.
* build variety). See design.mps.protocol.if.must-be-a. It is like
* C++ "dynamic_cast" with an assert.
*
* MustBeA_CRITICAL is like MustBeA for use on the critical path,
* where it does no checking at all in production builds. See
* design.mps.protocol.if.must-be-a.critical.
*/
#define CouldBeA(class, inst) ((INST_TYPE(class))(inst))
@ -234,7 +280,7 @@ extern void ClassRegister(InstClass class);
*
* The following are macros because of the need to cast subtypes of
* InstClass. Nevertheless they are named as functions. See
* <design/protocol/#introspect.c-lang>.
* design.mps.protocol.introspect.
*/
#define SuperclassPoly(kind, class) \
@ -256,10 +302,8 @@ extern void ClassRegister(InstClass class);
/* SetClassOfPoly -- set the class of an object
*
* This should only be used when specialising an instance to be a
* member of a subclass. Each Init function should call its
* superclass init, finally reaching InstInit, and then, once it has
* set up its fields, use SetClassOfPoly to set the class and check
* the instance with its check method. Compare with design.mps.sig.
* member of a subclass, once the instance has been initialized. See
* design.mps.protocol.if.set-class.
*/
#define SetClassOfPoly(inst, _class) \
@ -269,7 +313,8 @@ extern void ClassRegister(InstClass class);
/* Method -- method call
*
* Use this macro to call a method on a class, rather than accessing
* the class directly. For example:
* the class directly. See design.mps.protocol.if.method. For
* example:
*
* res = Method(Land, land, insert)(land, range);
*/

View file

@ -160,28 +160,23 @@ _`.overview.superclass`: Each class contains a ``superclass`` field.
This enables classes to call "next-method".
_`.overview.next-method`: A specialized method in a class can make use
of an overridden method from a superclass by accessing the method from
the appropriate field in the superclass object and calling it. The
macro ``NextMethod`` is provided for calling next methods.
of an overridden method from a superclass using the ``NextMethod``
macro, statically naming the superclass.
_`.overview.next-method.naive`: In some cases it is necessary to write
a method which is designed to specialize an inherited method, needs to
call the next-method, and yet the implementation doesn't have static
knowledge of the superclass. This might happen because the specialized
method is designed to be reusable by many class definitions. The
specialized method can usually locate the class object from one of the
parameters passed to the method. It can then access the superclass
through the ``superclass`` field of the class, and hence call the next
method. This technique has some limitations and doesn't support longer
method chains. It is also dependent on none of the class definitions
which use the method having any subclasses.
_`.overview.next-method.dynamic`: It is possible to write a method
which does not statically know its superclass, and call the next
method by extracting a class from one of its arguments using
``ClassOfPoly`` and finding the superclass using ``SuperclassPoly``.
Debug pool mixins do this. However, this is not fully general, and
combining such methods is likely to cause infinite recursion. Take
care!
_`.overview.access`: Classes must be initialized by calls to
functions, since it is these function calls which copy properties from
superclasses. Each class must provide an "ensure" function, which
returns the canonical copy of the class. The canonical copy may reside
in static storage, but no MPS code may refer to that static storage by
name.
functions, since there is no way to express overrides statically in
C89. ``DEFINE_CLASS`` defines an "ensure" function that initializes
and returns the canonical copy of the class. The canonical copy may
reside in static storage, but no MPS code may refer to that static
storage by name.
_`.overview.init`: In addition to the "ensure" function, each class
must provide an "init" function, which initialises its argument as a
@ -192,7 +187,7 @@ _`.overview.naming`: There are some strict naming conventions which
must be followed when defining and using classes. The use is
obligatory because it is assumed by the macros which support the
definition and inheritance mechanism. For every kind ``Foo``,
we insist upon the following naming conventions:-
we insist upon the following naming conventions:
* ``Foo`` names a type that points to a ``FooStruct``.
@ -215,9 +210,9 @@ Class declaration
``DECLARE_CLASS(kind, className)``
_`.int.declare-class`: Class declaration is performed by the macro
_`.if.declare-class`: Class declaration is performed by the macro
``DECLARE_CLASS``, which declares the existence of the class
definition elsewhere. It is intended for use where in headers.
definition elsewhere. It is intended for use in headers.
Class definition
@ -225,7 +220,7 @@ Class definition
``DEFINE_CLASS(kind, className, var)``
_`.int.define-class`: Class definition is performed by the macro
_`.if.define-class`: Class definition is performed by the macro
``DEFINE_CLASS``. A call to the macro must be followed by a function
body of initialization code. The parameter ``className`` is used to
name the class being defined. The parameter ``var`` is used to name a
@ -239,56 +234,81 @@ storage for the canonical class object, and some other things to
ensure the class gets initialized exactly once.
Class access
............
``CLASS(className)``
_`.if.class`: To get the canonical class object, use the ``CLASS``
macro, e.g. ``CLASS(Land)``.
Single inheritance
..................
``INHERIT_CLASS(this, className, parentName)``
_`.int.inheritance`: Class inheritance details must be provided in the
class initialization code (see `.int.define-class`_). Inheritance is
_`.if.inheritance`: Class inheritance details must be provided in the
class initialization code (see `.if.define-class`_). Inheritance is
performed by the macro ``INHERIT_CLASS``. A call to this macro will
make the class being defined a direct subclass of ``parentClassName``
by ensuring that all the fields of the parent class are initialized by
the parent class, and setting the superclass field of ``this`` to be
the canonical parent class object. The parameter ``this`` must be the
the same kind as ``parentClassName``.
by ensuring that all the fields of the embedded parent class (pointed
to by the ``this`` argument) are initialized as the parent class, and
setting the superclass field of ``this`` to be the canonical parent
class object. The parameter ``this`` must be the the same kind as
``parentClassName``.
Specialization
..............
_`.int.specialize`: Class specialization details must be given
_`.if.specialize`: Fields in the class structure must be assigned
explicitly in the class initialization code (see
`.int.define-class`_). This must happen *after* the inheritance
details are given (see `.int.inheritance`_).
`.if.define-class`_). This must happen *after* inheritance details
are given (see `.if.inheritance`_), so that overrrides work.
Extension
.........
_`.int.extend`: To extend the protocol when defining a new class, a
_`.if.extend`: To extend the protocol when defining a new class, a
new type must be defined for the class structure. This must embed the
structure for the primarily inherited class as the first field of the
structure. Class extension details must be given explicitly in the
class initialization code (see `.int.define-class`_). This must happen
*after* the inheritance details are given (see `.int.inheritance`_).
structure. Extension fields in the class structure must be assigned
explicitly in the class initialization code (see
`.if.define-class`_). This should be done *after* the inheritance
details are given for consistency with `.if.inheritance`_. This is,
in fact, how all the useful classes extend ``Inst``.
_`.int.extend.kind`: In addition, a class must be define for the new
_`.if.extend.kind`: In addition, a class must be defined for the new
kind of class. This is just an unspecialized subclass of the kind of
the class being specialized by the extension. For example::
typedef struct LandClassStruct {
InstClassStruct instClass; /* inherited class */
LandInsertMethod insert;
...
} LandClassStruct;
DEFINE_CLASS(Inst, LandClass, class)
{
INHERIT_CLASS(class, LandClass, InstClass);
}
DEFINE_CLASS(Land, Land, class)
{
INHERIT_CLASS(&class->instClass, Land, Inst);
class->insert = landInsert;
...
}
Methods
.......
``Method(kind, inst, meth)``
_`.int.method`: To call a method on an instance of a class, use the
_`.if.method`: To call a method on an instance of a class, use the
``Method`` macro to retrieve the method. This macro may assert if the
class is not of the kind requested. For example, to call the
``insert`` method on ``land``::
@ -298,7 +318,7 @@ class is not of the kind requested. For example, to call the
``NextMethod(kind, className, meth)``
_`.int.next-method`: To call a method from a superclass of a class,
_`.if.next-method`: To call a method from a superclass of a class,
use the ``NextMethod`` macro to retrieve the method. This macro may
assert if the superclass is not of the kind requested. For example,
the function to split AMS segments wants to split the segments they
@ -310,15 +330,22 @@ are based on, so does::
Conversion
..........
``IsA(className, inst)``
_`if.isa`: Returns non-zero iff the class of ``inst`` is a member of
the class or any of its subclasses.
``MustBeA(className, inst)``
_`.int.must-be-a`: To convert the C type of an instance to that of a
_`.if.must-be-a`: To convert the C type of an instance to that of a
compatible class (the class of the actual object or any superclass),
use the ``MustBeA`` macro. In hot varieties this macro performs a
fast dynamic type check and will assert if the class is not
compatible. In cool varieties, the class check method is called on
the object. For example, in a specialized Land method in the CBS
class::
compatible. It is like C++ "dynamic_cast" with an assert. In cool
varieties, the class check method is called on the object. For
example, in a specialized Land method in the CBS class::
static Res cbsInsert(Range rangeReturn, Land land, Range range)
{
@ -328,20 +355,23 @@ class::
``MustBeA_CRITICAL(className, inst)``
_`.int.must-be-a.critical`: For use when the cost of a type check is
too expensive in hot varieties, use ``MustBeA_CRITICAL`` in place of
_`.if.must-be-a.critical`: When the cost of a type check is too
expensive in hot varieties, use ``MustBeA_CRITICAL`` in place of
``MustBeA``. This only performs the check in cool varieties. Compare
with ``AVER_CRITICAL``.
``CouldBeA(className, inst)``
_`.int.could-be-a`: To make an unsafe conversion equivalent to
_`.if.could-be-a`: To make an unsafe conversion equivalent to
``MustBeA``, use the ``CouldBeA`` macro. This is in effect a simple
pointer cast, but it expresses the intention of class compatibility in
the source code. It is mainly intended for use when initializing an
object, when a class compatibility check would fail, or in debugging
code such as describe methods, where asserting is inappropriate.
object, when a class compatibility check would fail, when checking an
object, or in debugging code such as describe methods, where asserting
is inappropriate. It is intended to be equivalent to the C++
``static_cast``, although since this is C there is no actual static
checking, so in fact it's more like ``reinterpret_cast``.
Introspection
@ -365,7 +395,7 @@ functions.
``SuperclassPoly(kind, class)``
_`.int.superclass`: An introspection function which returns the direct
_`.if.superclass`: An introspection function which returns the direct
superclass of class object ``class`` as a class of kind ``kind``.
This may assert if the superclass is not (a subtype of) the kind
requested.
@ -373,22 +403,26 @@ requested.
``ClassOfPoly(kind, inst)``
_`.int.class`: An introspection function which returns the class of
which ``inst`` is a direct instance, as a classes of kind ``kind``.
_`.if.class`: An introspection function which returns the class of
which ``inst`` is a direct instance, as a class of kind ``kind``.
This may assert if the class is not (a subtype of) the kind requested.
``SetClassOfPoly(inst, class)``
_`int.set-class`: An initialization function that sets the class of
_`.if.set-class`: An initialization function that sets the class of
``inst`` to be ``class``. This is intended only for use in
initialization functions, to specialize the instance once its fields
have been initialized.
have been initialized. Each Init function should call its superclass
init, finally reaching InstInit, and then, once it has set up its
fields, use SetClassOfPoly to set the class and check the instance
with its check method. Compare with `design.mps.sig`_.
.. _`design.mps.sig`: sig
``IsSubclassPoly(sub, super)``
``IsSubclass(sub, super)``
_`.int.subclass`: An introspection function which returns a ``Bool``
_`.if.subclass`: An introspection function which returns a ``Bool``
indicating whether ``sub`` is a subclass of ``super``. That is, it is
a predicate for testing subclass relationships.
@ -396,31 +430,17 @@ a predicate for testing subclass relationships.
Protocol guidelines
...................
_`.guide.fail`: When designing an extensible function which might
fail, the design must permit the correct implementation of the
failure-case code. Typically, a failure might occur in any method in
the chain. Each method is responsible for correctly propagating
failure information supplied by superclass methods and for managing
it's own failures.
_`.guide.fail.before-next`: Dealing with a failure which is detected
before any next-method call is made is similar to a fail case in any
non-extensible function. See `.example.fail`_ below.
_`.guide.fail.during-next`: Dealing with a failure returned from a
next-method call is also similar to a fail case in any non-extensible
function. See `.example.fail`_ below.
_`.guide.fail.after-next`: Dealing with a failure which is detected
after the next methods have been successfully invoked is more complex.
If this scenario is possible, the design must include an
"anti-function", and each class must ensure that it provides a method
for the anti-method which will clean up any resources which are
claimed after a successful invocation of the main method for that
class. Typically the anti-function would exist anyway for clients of
the protocol (for example, "finish" is an anti-function for "init").
The effect of the next-method call can then be cleaned up by calling
the anti-method for the superclass. See `.example.fail`_ below.
_`.guide.fail`: When designing an extensible method which might fail,
the design must permit the correct implementation of the failure-case
code. Typically, a failure might occur in any method in the chain.
Each method is responsible for correctly propagating failure
information supplied by superclass methods and for managing it's own
failures. This is not really different from the general MPS
convention for unwinding on error paths. It implies that the design
of a class must include an anti-method for each method that changes
the state of an instance (e.g. by allocating memory) to allow the
state to be reverted in case of a failure. See `.example.fail`_
below.
Example
@ -428,15 +448,14 @@ Example
_`.example.inheritance`: The following example class definition shows
both inheritance and specialization. It shows the definition of the
class ``EPDRPool``, which inherits from ``EPDLPool`` of kind ``Pool``
and has specialized values of the ``name``, ``init``, and ``alloc``
fields. ::
class ``RankBuf``, which inherits from ``SegBuf`` of kind ``Seg``
and has specialized ``varargs`` and ``init`` method. ::
DEFINE_CLASS(Pool, EPDRPool, this)
DEFINE_CLASS(Buffer, RankBuf, class)
{
INHERIT_CLASS(this, EPDRPool, EPDLPool);
this->init = EPDRInit;
this->alloc = EPDRAlloc;
INHERIT_CLASS(class, RankBuf, SegBuf);
class->varargs = rankBufVarargs;
class->init = rankBufInit;
}
_`.example.extension`: The following (hypothetical) example class
@ -467,49 +486,57 @@ checking properties of the pool. ::
_`.example.fail`: The following example shows the implementation of
failure-case code for an "init" method, making use of the "finish"
anti-method::
anti-method to clean-up a subsequent failure. ::
static Res mySegInit(Seg seg, Pool pool, Addr base, Size size,
ArgList args)
static Res AMSSegInit(Seg seg, Pool pool,
Addr base, Size size,
ArgList args)
{
MYSeg myseg;
OBJ1 obj1;
Res res;
Arena arena;
AMS ams = MustBeA(AMSPool, pool);
Arena arena = PoolArena(pool);
AMSSeg amsseg;
Res res;
AVERT(Pool, pool);
arena = PoolArena(pool);
/* Initialize the superclass fields first via next-method call */
res = NextMethod(Seg, AMSSeg, init)(seg, pool, base, size, args);
if (res != ResOK)
goto failNextMethod;
amsseg = CouldBeA(AMSSeg, seg);
/* Ensure the pool is ready for the segment */
res = myNoteSeg(pool, base);
if(res != ResOK)
goto failNoteSeg;
amsseg->grains = size >> ams->grainShift;
amsseg->freeGrains = amsseg->grains;
amsseg->oldGrains = (Count)0;
amsseg->newGrains = (Count)0;
amsseg->marksChanged = FALSE; /* <design/poolams/#marked.unused> */
amsseg->ambiguousFixes = FALSE;
/* Initialize the superclass fields first via next-method call */
res = NextMethod(Seg, MySeg, init)(seg, pool, base, size, args);
if(res != ResOK)
goto failSuperInit;
myseg = CouldBeA(MySeg, seg);
res = amsCreateTables(ams, &amsseg->allocTable,
&amsseg->nongreyTable, &amsseg->nonwhiteTable,
arena, amsseg->grains);
if (res != ResOK)
goto failCreateTables;
/* Create an object after the next-method call */
res = ControlAlloc(&obj1, arena, sizeof(OBJ1Struct));
if(res != ResOK)
goto failObj1;
/* start off using firstFree, see <design/poolams/#no-bit> */
amsseg->allocTableInUse = FALSE;
amsseg->firstFree = 0;
amsseg->colourTablesInUse = FALSE;
myseg->obj1 = obj1
amsseg->ams = ams;
RingInit(&amsseg->segRing);
RingAppend((ams->allocRing)(ams, SegRankSet(seg), size),
&amsseg->segRing);
/* Finished initialization, so specialize the segment. */
SetClassOfPoly(seg, CLASS(MySeg));
return ResOK;
SetClassOfPoly(seg, CLASS(AMSSeg));
amsseg->sig = AMSSegSig;
AVERC(AMSSeg, amsseg);
failObj1:
/* call the anti-method for the superclass */
NextMethod(Seg, MySeg, finish)(seg);
failSuperInit:
/* reverse the effect of myNoteSeg */
myUnnoteSeg(pool, base);
failNoteSeg:
return res;
return ResOK;
failCreateTables:
NextMethod(Seg, AMSSeg, finish)(seg);
failNextMethod:
AVER(res != ResOK);
return res;
}
@ -518,63 +545,34 @@ Implementation
_`.impl.derived-names`: The ``DEFINE_CLASS()`` macro derives some
additional names from the class name as part of it's implementation.
These should not appear in the source code - but it may be useful to
These should not appear in the source code, but it may be useful to
know about this for debugging purposes. For each class definition for
class ``SomeClass`` of kind ``SomeKind``, the macro defines the
following:
* ``extern SomeKind SomeClassGet(void);``
The class accessor function. See `.overview.naming`_. This function
contains local static storage and a guardian to ensure the storage
is initialized exactly once.
The class ensure function. See `.overview.naming`_. This function
handles local static storage for the canonical class object and a
guardian to ensure the storage is initialized exactly once. This
function is invoked by the ``CLASS`` macro (`.if.class`_).
* ``static void SomeClassInit(SomeKind);``
A function called by ``SomeClassGet()``. All the class
initialization code is actually in this function.
_`.impl.init-once`: Class objects only behave according to their
definition after they have been initialized, and class protocols may
not be used before initialization has happened. The only code which is
allowed to see a class object in a partially initialized state is the
initialization code itself -- and this must take care not to pass the
object to any other code which might assume it is initialized. Once a
class has been initialized, the class might have a client. The class
must not be initialized again when this has happened, because the
state is not necessarily consistent in the middle of an initialization
function. The initialization state for each class is stored in a
Boolean "guardian" variable local to the accessor function. This
ensures the initialization happens only once. The path through the
``SomeClassGet`` function should be very fast for the common case when
this variable is ``TRUE``, and the class has already been initialized,
as the canonical static storage can simply be returned in that
case. However, when the value of the guardian is ``FALSE``, the class
is not initialized. In this case, a call to ``SomeClassGet`` must
first execute ``SomeClassInit`` and then set the guardian to
``TRUE``. However, this must happen atomically (see
`.impl.init-lock`_).
_`.impl.init-lock`: There would be the possibility of a race condition
if ``SomeClassGet`` were called concurrently on separate threads
before ``SomeClass`` has been initialized. The class must not be
initialized more than once, so the sequence test-guard, init-class,
set-guard must be run as a critical region. It's not sufficient to use
the arena lock to protect the critical region, because the class
object might be shared between multiple arenas. The ``DEFINE_CLASS()``
macro uses a global recursive lock instead. The lock is only claimed
after an initial unlocked access of the guard variable shows that the
class is not initialized. This avoids any locking overhead for the
common case where the class is already initialized. This lock is
provided by the lock module -- see design.mps.lock_.
.. _design.mps.lock: lock
_`.impl.subclass`: The subclass test `.int.subclass`_ is implemented
_`.impl.subclass`: The subclass test `.if.subclass`_ is implemented
using a compact array of superclasses [Cohen_1991]_ giving a fast
constant-time test. RB_ tried an approach using prime factors
constant-time test. (RB_ tried an approach using prime factors
[Gibbs_2004]_ but found that they overflowed in 32-bits too easily to
be useful.
be useful.) Each class is assigned a small integer id, and a "level"
which is the distance from the root of the class hierarchy. The
``InstClass`` structure contains an array of these ids indexed by
level, representing the inheritance of this class. A class is a
subclass of another if and only if the superclass id is present in the
array at the superclass level. The level and the id are statically
defined using enum constants, so the test is fast and simple.
A. References
@ -606,7 +604,12 @@ B. Document History
- 2016-04-08 RB_ Substantial reorgnisation.
- 2016-04-13 RB_ Writing up overview of kinds, with explanation of class extension. Writing up ``Method``, ``NextMethod``, ``SetClassOfPoly``, ``MustBeA``, etc. and updating the descriptions of some older interface. Updating the example.
- 2016-04-13 RB_ Writing up overview of kinds, with explanation of
class extension. Writing up ``Method``, ``NextMethod``,
``SetClassOfPoly``, ``MustBeA``, etc. and updating the descriptions
of some older interface. Updating the example.
- 2016-04-19 RB_ Miscellaneous clean-up in response to review by GDR_.
.. _RB: http://www.ravenbrook.com/consultants/rb/
.. _GDR: http://www.ravenbrook.com/consultants/gdr/