Go to the next section.
(with contributions from Doug Cutting, Frank Halasz, and Farrell Wymore)
(typeset 2 March 1995)
Copyright (C) 1993--1995 Xerox Corporation
All Rights Reserved.
This document describes version 1.8 of the Inter-Language Unification (ILU) system.
We gratefully acknowledge the contributions of many people, including our reviewers, alpha and beta testers, and regular users. The list includes (but is not limited to): Maria Perez Ayo, Mike Beasley, Erik Bennett, David Brownell, Bruce Cameron, George Carrette, Philip Chou, Daniel W. Connolly, Paul Everitt, Josef Fink, Steve Freeman, Mark Friedman, Gabriel Sanchez Gutierrez, Bruno Haible, Scott W. Hassan, Carl Hauser, Andrew Herbert, Angie Hinrichs, Ben Hurwitz, Roberto Invernici, Swen Johnson, Gabor Karsai, Nobu Katayama, Sangkyun Kim, Ted Kim, Don Kimber, Dan Larner, Carsten Malischewski, Larry Masinter, Fernando D. Mato Mira, Fazal Majid, Steven D. Majewski, Masashige Mizuyama, Curtis McKelvey, Chet Murthy, Farshad Nayeri, Les Niles, T. Owen O'Malley, Andreas Paepcke, Karin Petersen, Joerg Schreck, Ian Smith, Peter Swain, Marvin Theimer, Lindsay Todd, P. B. Tune, Kevin Tyson, Bill van Melle, Brent Welch.
ILU is primarily about interfaces between units of program structure; we call them by the generic term modules. They could be parts of one process, all written in the same language; they could be parts written in different languages, sharing runtime support in one memory image; they could be parts running in different memory images on different machines (on different sides of the planet). A module could even be a distributed system implemented by many programs on many machines. A particular module might be part of several different programs at the same time. ILU provides a way to define the interfaces for these modules, and facilitates using a module from a number of different languages. It optimizes calls across module interfaces to involve only as much mechanism as necessary for the calling and called modules to interact. In particular, when the two modules are in the same memory image and use the same data representations, the calls are direct local procedure calls -- no stubs or other RPC mechanisms are involved. The notion of a `module' should not be confused with the independent concept of a program instance; by which we mean the combination of code and data running in one memory image. A UNIX process is (modulo the possibilities introduced by the ability, in some UNIX sytems, to share memory between processes) an example of a program instance.
The approach used by ILU is one common to standard RPC systems such as Sun's ONC RPC, Xerox's Courier, and most implementations of OMG's CORBA. An interface is described once in ILU's `language-neutral' Interface Specification Language (called, simply, ISL). Types and exceptions can be defined in this specification. Exported functionality is specified by defining methods on object types. For each of the particular programming languages supported by ILU, a version of the interface in that particular programming language can be generated; the ISL description may (in a future version of ISL) indicate choices taken for how to cast the interface in the particular programming languages. Also generated are stubs for binding and calling; these stubs can bind to, call, and be called from stubs generated (from the same ISL description) for a different programming language. These stubs are generated in such a way that applications which link a caller and callee written in the same language directly together suffer no calling overhead. This makes ILU useful for defining interfaces between modules even in programs that do not use RPC.
ILU is object-oriented. By this, we mean that object types serve as the primary encapsulation mechanism in ILU. All functionality is exported from a module as methods that can be invoked on an instance of some object type. The object types provide the context in which methods are executed. The object system also provides inheritance, to aid in structuring of interfaces.
With respect to a particular ILU object instance, a module is called the server if it implements the methods of that object, or a client if it calls, but does not implement, the methods of that object. One module can thus be a client of one object, and the server of another. An ILU object can be passed as a parameter to or result of a method call, and can be (in) the parameter to an exception. An object may be passed from its server to a client, from a client to its server, or between two clients, in any of the above three kinds of position. Unlike some RPC systems, there can be multiple ILU objects of the same type, even on one machine, even within one program instance.
For a given ILU object, there will, in general, be multiple language-specific objects; each is an "object" in one of the programming languages used in the system. One language-specific object, designated the true object, actually provides the implementation of the ILU object; it is thus part of the server module. The true object's methods are written by the programmer, not generated by ILU. The other language-specific objects are surrogate objects; their methods are actually RPC stubs (generated by ILU) that call on the true object. A surrogate object is used by a client module when the server module is in a different program instance or uses different data representations.
To use (e.g., call the methods of) an ILU object, a client must first obtain a language-specific object for that ILU object. This can be done in one of two ways: (1) the client can call on a language-specific object of a different ILU object to return the object in question (or receive the object in a call made on the client, or in the parameter of an exception caught and handled by the client); or (2) certain standard facilities can be used to acquire a language-specific object given either addressing or naming information about the ILU object. The addressing information is called a string binding handle (SBH), and the ILU runtime library includes a procedure to acquire a language-specific object given a string binding handle for an ILU object (in strongly-typed languages, this procedure is typed to return an object of the base type common to all ILU objects in that language).
Every creation of a surrogate instance implies communication with the server module, and binding of the surrogate instance to the true instance. ILU may attempt to perform this communication when it is actually necessary, rather than immediately on surrogate instance creation.
The process of creating an instance may bootstrapped via a name
service, such as the PARC Name-and-Maintenance-Server
(NMS
), which allows servers to register instances on a
net-wide basis. A server registers a mapping from naming information to
a string binding handle. The client-side stubs for an interface include
a procedure that takes naming information, looks up the corresponding
string binding handle in the name service, and calls the above-mentioned
library routine to map the SBH to a language-specific object.
Alternatively, a client can do those steps itself, using an ILU
runtime library procedure to acquire a language-specific object for the
name service.
Many existing RPC protocols and servers do not have the notion of multiple instances of a type co-existing at the same server, so cannot use the instance discrimination information passed in ILU procedure calls. To support the use of these protocols and servers, we introduce the notion of a singleton object type, of which there is only one instance (of each singleton type) at a kernel server. Note that because a single address space may support multiple kernel servers, this means that in a single address space, there may be multiple instances of the same singleton type. When a method is being called on an instance of a singleton type, no instance discrimination information is passed. Singleton types may not be subclassed.
ILU objects can be passed as parameters in calls on methods of other ILU objects, as return values from such calls, or in parameters of exceptions ILU raised by such calls. When an object is marshalled for RPC, it is represented by its string binding handle and its most specific type ID. A string binding handle for an object has the form
instance-handle@
server-id@
protocol-info|
transport-info
where instance-handle is a string containing only mixed-case alphanumeric characters and the period ('.') character; server-id is a string containing only mixed-case alphanumeric characters and the period ('.') character; protocol-info describes the RPC protocol used by the service, along with any protocol-specific information; transport-info describes the network communication transport layer used to communicate with the server (what about lexicographic restrictions?).
The tuple instance-handle
is also known as the object ID (OID).
It uniquely identifies an ILU object. The
server-id uniquely identifies the server containing the true
object, and the instance-handle identifies the true object
within the server in some server-specific way. The only significance
of the division between the server-id and the
instance-handle is that two ILU objects are
considered "siblings"
(see later) exactly when their
server-ids are equal.
@
server-id
The tuple protocol-info|transport-info
is also known as the contact info.
It describes the location and communications protocol used by a client
to talk with the server. Note that while the design of
object IDs allows a service to be replicated, the
contact info provides only one way to contact the service;
this is a bug, to be fixed in the future by allowing multiple contact
infos, and indirect contact infos, in a string binding handle.
(1)
The object model specified here provides for multiple inheritance. It is intended that the subtype provide all the methods described by its supertypes, plus possibly other methods described directly in the subtype description. It is expected that in languages which support single-inheritance (or better) object models, that an ILU inheritance tree will be reflected in the language-specific inheritance tree.
In the ILU type system, the only subtyping questions that arise are between two object types. This is because ILU employs only those OOP features common to all languages supported.
Subtyping in ILU is based on structure and name; we include the names in the structure, and thus need only talk about structure. An object type declaration of the form defined later constructs a structure of the form
(OBJTYPE SINGLETON: singleton-protocol-info OPTIONAL:Boolean
COLLECTIBLE:Boolean
AUTHENTICATION: authentication-type SUPERTYPES: supertype-structure, ... METHODS: method-structure, ... LEVEL-BRANDS: (interface-name, interface-brand, type-name, type-brand))
Structure A is a subtype of structure B iff either (1) A and B are equal structures, or (2) one member of A's supertype-structures is a subtype of B.
Note that the level-brands include the interface name and (optional) brand, as well as the name and (optional) brand of the type being declared. Thus, two declarations of subtypes of the same type normally create distinct subtypes, because they would declare types of different names, or in interfaces with different names. When the interface name and the type name are the same, this does not cause a distinction, although other structural differences might. If the programmer wants to indicate that there's a semantic distinction, even though it doesn't otherwise show up in the structure, s/he can use different interface brands and/or different type brands. These distinctions can be made between declarations in different files, or between successive versions of a declaration in a file that's been edited.
Some ILU object instances may have implementation
dependencies on private communication with other instances. For
example, imagine an object type time-share-system
, which
provides the method ListUsers()
, which returns a list of
"user" instances. Imagine that time-share-system
also
provides the method SetUserPriority(u : user, priority : integer)
.
We would like to be able to provide some assurance that
the user instance used as a parameter to SetUserPriority
is an
instance returned from a call to ListUsers
on the same instance
of a time-share-system
, because the way in which
SetUserPriority
is implemented relies on the user being a user
of that particular time-share-system
.
The ILU model provides the notion of a sibling object. Two instances are siblings if their methods are handled by the same server. Instances that are non-discriminator parameters to methods may be specified in ISL as having to be siblings of the discriminator.
A simple form of garbage collection is defined for ILU objects. If an object type is tagged as being collectible, a server that implements objects of that type expects clients holding surrogate instances to register with it, passing an instance of a callback object. When a client finishes with the surrogate, the client unregisters itself. Thus the server may maintain a list of clients that hold surrogate instances. If no client is registered for an object, and the object has been dormant (had no methods called on it) for a period of time T1, the server may feel free to garbage collect the instance. T1 is determined by human concerns, not network performance: T1 is set long enough to allow useful debugging of a client.
To deal with possible failure of a client process, we introduce another time-out parameter. If an instance with registered clients has been dormant for a period of time T2, the server uses the callback instance associated with each client to see if the client still exists. If the client cannot be contacted for the callback, the server may remove it from the list of registered clients for that instance.
If a client calls a method on a surrogate instance of a true instance which
has been garbage-collected (typically because of partitioning), it will
receive the ilu.ProtocolError
exception, with detail code
ilu.NoSuchInstanceAtServer
.
ILU uses the notion of an exception to signal errors between modules. An exception is a way of passing control outside the normal flow of control. It is typically used for handling of errors. The routine which detects the error signals an exception, which is caught by some error-handling mechanism. The exception type supported in ILU is a termination-model exception, in which the calling stack is unrolled back to the frame which defined the exception handler. Exceptions are signalled and caught using the native exception mechanisms for the servers and clients. A raised exception may carry a single parameter value, which is typed.
The type and exception model used by ILU is quite similar to that used by the Object Management Group's Common Object Request Broker Architecture (CORBA). We have in fact changed ILU in some ways to more closely match CORBA. Our tools will optionally parse the OMG's Interface Definition Language (OMG IDL) as well as ILU's ISL.
ILU also attempts to address issues that are already upon us, but are not addressed in CORBA 1.2: 64-bit architectures, UNICODE characters, a uniform way of indicating optional values, and garbage collection.
ISL has a different syntax from OMG IDL for two reasons: firstly, the C/TIRPC/C++ influences in OMG IDL are neither easy to read nor conducive to rational thought about mappings of CORBA to non-C languages; secondly, the obvious difference of ISL may help to avoid confusion about what language an interface definition is really written in, and thus about what concepts may or may not be expressed or expected. As all concepts present in ILU but not in CORBA must be used through ISL, a user who conscientiously sticks to OMG IDL can be assured that he is not using any proprietary extensions in expressing his interfaces.
ILU does not yet provide some of the features required by a full CORBA implementation. Notably it does not provide a Dynamic Invocation Interface, or implementations of either Interface Repository or Implementation Repository. It does not provide the Basic Object Adapter interface, either, but does provide an object adapter with most of the BOA's capabilities, except for those connected with the Interface Repository and/or Implementation Repository.
A number of concepts in CORBA that seem to require further
thought are not yet directly supported in ILU: the use of
#include
(ILU uses a more limited notion of "import");
the notion of using an IDL "interface" as both an object
type and a name space (this seems to be a "tramp idea" from the
language C++; in ILU the "interface" defines a
name space, and the object type defines a type); the notion that all BOA
objects are persistent (in ILU, the question of whether an
object is persistent is left up to that object's implementation); the
notion that type definitions can exist outside the scope of any module
or namespace (in ILU, all definitions occur in some interface).
Currently, there is no support in ILU for CORBA
context
s, the OMG IDL type any
, or the
OMG IDL type Object
. We feel that all three of these
notions tend to weaken interface descriptions.
Go to the next section.