Go to the previous, next section.

ILU Customization

Introduction

ILU includes a number of internal interfaces that allow various functionality of the ILU kernel library to be replaced by user functionality.

Most of these interfaces are defined in two ILU source files, `ILUSRC/runtime/kernel/iluxport.h' and `ILUSRC/runtime/kernel/iluntrnl.h'. We will not attempt to duplicate that documentation here, to avoid the inevitable errors when documentation is provided in two different forms; rather, this section of the manual will provide sketches of the interfaces, and refer the reader to the appropriate header files. For any discrepancies noted between the material here, and the material in the header files, the header files should be assumed to the `truth'.

Event Loops and Threads

Every ILU address space uses either real threads, or some sort of event dispatching loop to simulate threads. ILU is thread-safe internally, and by default will continue to check its usage for even when event dispatching is used. Since many different thread systems and event dispatching loops exist, ILU provides interfaces to allow the user to describe the particular one that they're using to the ILU kernel. See section section Threads and Event Loops for more information on these topics. See `ILUSRC/runtime/kernel/iluxport.h' for documentation of the interfaces.

RPC Protocols and Data Transport Mechanisms

The ILU remote procedure call mechanism operates in layers.

ILU includes registration mechanisms to allow applications to add to the kinds of RPC protocols and data transport mechanisms that can be used.

RPC Protocols

Each ilu_Protocol object reflects a particular mapping of the abstract ILU RPC protocol onto a specific externally-defined RPC protocol. (See section Protocols and Transports for a discussion of the abstract ILU protocol, and how it is mapped to the ONC RPC protocol, and to the XNS Courier protocol.)

New RPC message protocols can be added to the ILU kernel by writing a new ilu_Protocol object, and calling the ILU kernel function ilu_RegisterProtocol(). register it. The structure and requirements of an ilu_Protocol object are defined in the file `ILUSRC/runtime/kernel/protocol.h'; the methods of the protocol are considered to be `inside' the ILU kernel, and must therefore conform to all ILU locking and error conventions. The locking conventions are discussed in `ILUSRC/runtime/kernel/iluxport.h'; the error conventions are documented in
`ILUSRC/runtime/kernel/iluerror.h', and pre-defined errors are documented in
`ILUSRC/runtime/kernel/iluerrs.h'.

Various examples of ILU protocols are available for study:

Transport Filters

In general, ILU protocols form `messages' consisting of sequences of bytes, which are then passed to the ILU transport layer to be conveyed to another address space. The transport layer itself is composed of one or more ILU transport filters, each of which handles the message in turn. These filters are either communication filters, such as the filters which actually convey messages via TCP/IP or UDP/IP, or transformation filters, which alter the message and pass it to another transport filter, such as the ONC RPC record-marking filter, or the secure transport filter.

Each transport filter is either reliable or unreliable. All transformation filters are reliable; communication filters may or may not be reliable. A communication filter is reliable if it guarantees that any messages handed to it for transport will be reliably delivered to the other end of the communication connection. This in turn means that the communication mechanism used by the transport will take care of timeouts, retries, etc., internally, so that the ILU application need not worry about these itself. Unreliable communication filters are those which may require ILU participation in timeout and resending of messages to achieve reliable delivery.

Each filter is also either boundaried or non-boundaried. Boundaried filters are those which can comprehend and preserve message boundaries. Non-boundaried filters simply deal in chunks of bytes and have no way to recognize or preserve message boundaries. Various protocols and filters may have requirements as to whether the next filter below it in the communication stack is boundaried or non-boundaried.

New transport filters may be registered with the ILU kernel by calling the kernel function ilu_RegisterTransport(), described in `ILU/runtime/kernel/iluxport.h', with the name of a new transport filter and the address of a routine which returns an instance of the new transport object type. Implementing a new transport object type actually consists of implementing several related object types, including ilu_TransportCreator, ilu_TransportClass, and ilu_Mooring. These object types are defined in the file `ILUSRC/runtime/kernel/transprt.h'.

As with protocols, the methods of the transport filter are considered to be `inside' the ILU kernel, and must therefore conform to all ILU locking and error conventions. The locking conventions are discussed in `ILUSRC/runtime/kernel/iluxport.h'; the error conventions are documented in `ILUSRC/runtime/kernel/iluerror.h', and pre-defined errors are documented in `ILUSRC/runtime/kernel/iluerrs.h'.

Examples of transformation filters may be found in `ILUSRC/runtime/kernel/sunrpcrm.c', which is a boundaried filter implementing ONC RPC's TCP/IP record marking scheme, and `ILUSRC/runtime/kernel/security.c', which is a non-boundaried filter implementing message integrity, sender authentication, and message privacy. Examples of communication filters may be found in `ILUSRC/runtime/kernel/newtcp.c', which is a non-boundaried reliable filter implementing data communication via TCP/IP, `ILUSRC/runtime/kernel/udp.c', which is a non-boundaried unreliable filter implementing data communication via UDP/IP, and `ILUSRC/runtime/kernel/inmem.c', which is a boundaried reliable filter implementing intra-address-space communication via memory buffers.

Object Incarnation Procedures

ILU true objects live in kernel servers, a kernel data structure that handles communication and other aspects of the object implementation. When an object reference is received from another address space, the kernel server is responsible for mapping this reference to an actual object. Normally, the kernel server simply consults an internal hash table for an object corresponding to a specified `instance handle'; however, an application may register an application-specific callback function to be used instead. This allows on-the-fly creation of objects, which is often vital when handling many objects. Actual in-memory representations of the objects can be garbage-collected, then dynamically re-incarnated when needed by a client.

The application registers this functionality by creating an implementation of an ilu_ObjectTable object, and passing that implementation as a parameter to ilu_CreateTrueServer when creating the kernel server. Typically, ilu_CreateTrueServer is called directly only by a language-specific runtime; the actual application would work with object tables via whatever mechanism is exported by the language-specific runtime. Check the documentation for your particular language runtime for more information.

Object URLs

ILU regards string binding handles generically as a way of encoding four pieces of information: the instance handle for an object, the server ID for an object, the most-specific type ID (MSTID) for an object, and communication information about how to communicate with that object, which we call contact-info. It further restricts them to conform to the URL syntax specified in the World Wide Web Consortium and IETF standard RFC 1738 (http://www.w3.org/pub/WWW/Addressing/rfc1738.txt). But this still allows ILU to support any number of URL schemes, which we define as some way of encoding these four pieces of information which conforms to the URL syntax rules.

The default URL scheme is called ilu:, and encodes the information as

ilu:<server-id>/<instance-handle>;<MSTID>;<contact-info>

An application can register a parser for one or more application-specific URL schemes by calling the function ilu_RegisterSBHParser. It takes as an argument a function which will accept a URL string, and return the four components required by ILU. For instance, you might want to use a URL scheme for the OMG CORBA IIOP something like

iiop_1_0://<hostname>:<port>/<server-id>/<ih>

which can be considered to contain an instance handle of <ih>, a server ID of <server-id>, an implicit object type of IDL:omg.org/CORBA/Object:1.0, and contact-info of
iiop_1_0_1@tcp_<hostname>_<port>.

Or, you might want to use an HTTP URL for an ILU object which is exported via the HTTP ILU protocol. Suppose that the normal ILU string binding handle for the object was

ilu:tcp_1.2.3.4_20000//http_obj0;ilu:Ilu_Http_Type;http_1_0@tcp_1.2.3.4_20000

An alternate form which would be compatible with Web browsers would be

http://1.2.3.4:20000//http_obj0

with an implicit server ID of tcp_1.2.3.4_20000, an implicit MSTID of ilu:Ilu_Http_Type, an instance handle of /http_obj0, and contact-info of http_1_0@tcp_1.2.3.4_20000.

See the `ILUSRC/runtime/kernel/iluxport.h' for details on how to use
ilu_RegisterSBHParser.

Identity Types

As discussed in section Identities, application-specific identity types can be registered with the ILU kernel for use with various authorization and accounting schemes, and to support various forms of security in wire protocols and transports. An application does this by creating a new value of type ilu_IdentityType, as specified in `ILUSRC/runtime/kernel/iluxport.h', and calling the kernel function ilu_RegisterIdentityType. After this is done, values of the new identity types may be used. The major use for these identity types is to work together with new application-specific RPC protocols and message transports (described above), to implement various security and access policies for distributed systems.

Note that the mere act of registering a new identity type with the ILU kernel will not cause values of that identity type to be automatically transmitted in ILU calls. This will only happen if an appropriately designed transport or protocol, which knows to do this, is also used. For experimental purposes, we provide a switch will will cause the various flavors of Sun RPC implemented for ILU to automatically pass one specific identity type. To enable this, set the environment variable ILU_SUNRPC_PREFERRED_IDENTITY to the name of the identity type to be passed automatically before running any of your ILU programs.

By default, the Sun RPC protocols will automatically pass the UNIX identification of the caller (the user id, group id, host IP address, and list of groups to which the caller belongs). The identity type for this information is called "SunRPCAuthUnixIdentity". To prevent its being passed automatically, set the environment variable ILU_NO_SUNRPC_UNIX_AUTH to any value before running your ILU programs.

Malloc Failure Recovery

ILU uses a number of internal interfaces to allocate and free memory, such as ilu_malloc(), ilu_free(), and ilu_realloc(). These functions wrap calls to the standard malloc(), etc., in wrappers that allow for better error handling. They are documented in

`ILUSRC/runtime/kernel/iluxport.h'.

Applications can register callback functions to handle malloc failures, in two ways. The kernel function ilu_AddFreer() allows registration of routines which can be called to free up memory, to allow a malloc call to succeed. The kernel functions ilu_SetMemFaultAction() and ilu_SetMemFaultConsumer() allow applications to determine what action should be taken if a malloc failure occurs.

Error Reporting

ILU includes a comprehensive error-signalling system in the kernel library, which is documented in `ILUSRC/runtime/kernel/iluerror.h'. In addition, the kernel library contains many calls to _ilu_Assert, which check that various kernel invariants are maintained. When a runtime assertion fails, the kernel may either call an application-specified failure handler, set by a call to ilu_SetAssertionFailureConsumer(), or take one of three default actions, chosen by a call on ilu_SetAssertionFailureAction(). The three default actions are (1) to generate an illegal instruction trap, and thus coredump; (2) to exit with some error code; and (3) to enter an endless loop, calling sleep() repeatedly. The third action is the default action; the intent is to stop the program with all invalid data intact on the stack, and network connections intact, so that a debugger may attach to the `live' process.

Debugging Interfaces

The ILU kernel contains a large number of debugging print statements, which document various things going on inside the kernel. The specific things printed may be controlled by calling either ilu_SetDebugLevel() or ilu_SetDebugLevelViaString(). The specific bits which can be specified to ilu_SetDebugLevel(), or names which can be specified to ilu_SetDebugLevelViaString(), are documented `ILUSRC/runtime/kernel/iludebug.h'.

All debugging messages are displayed via calls to the kernel function ilu_DebugPrintf(). Normally, this routine simply calls vfprintf (stderr, ...) to actually output the messages. However, this can be changed to call some application-specific message output system by calling ilu_SetDebugMessageHandler(), documented in `ILUSRC/runtime/kernel/iluxport.h'. Two special values are defined for, and accepted by, ilu_SetDebugMessageHandler(); the value ILU_DEFAULT_DEBUG_MESSAGE_HANDLER causes the debug system to revert to the original output handler; the value ILU_NIL_DEBUG_MESSAGE_HANDLER causes the debug system to simply discard any debugging messages.

Debugging output can be directed to a file, by calling ilu_SendDebugOutputToFile() with a filename as an argument. The file will be created, and debugging messages will be written to it.

Go to the previous, next section.