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/iluntrnl.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 @br `ILUSRC/runtime/kernel/iluerror.h', and pre-defined errors are documented in @br `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/iluntrnl.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

It is sometimes useful to define application-specific URL syntax which carries some of the data implicitly. For instance, you might want to define a URL scheme for the OMG CORBA IIOP something like

iiop_1_0://<hostname>.<port>/<object-key>

with an implicit object type of IDL://omg.org/CORBA.Object:1.0. An application can register a parser for an application-specific URL scheme with the ILU kernel by calling the function ilu_RegisterSBHParser.

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.