.. mode: -*- rst -*-

C interface design
==================

:Tag: design.mps.interface.c
:Author: Richard Brooksby
:Date: 1996-07-29
:Status: incomplete document
:Revision: $Id: //info.ravenbrook.com/project/mps/version/1.113/design/interface-c.txt#1 $
:Copyright: See `Copyright and License`_.
:Index terms: pair: C interface; design


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-2014 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:

#. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

#. 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.

#. 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.**
