Go to the previous, next section.

Using ILU with Common Lisp

This document is for the Common Lisp programmer who wishes to use ILU. The first section explains the mappings from the ILU Interface Specification Language into the Common Lisp language, the second discusses concepts necessary for people exporting ILU modules from Common Lisp. In general, people who need only to use pre-existing ILU modules should only need to read the first section. You should understand the types and concepts supported by ILU before reading this document. (See section ILU Concepts.)

ILU Mappings to Common Lisp

Generating Common Lisp Surrogate and True Stubs

The program ILU lisp-stubber takes a interface specification (an `.isl' file) and generates lisp code to provide both client-side and server-side support for the interface. The files are generated in the current working directory. In particular, the following files are generated:

Packages & Symbols

Runtime code is in the Common Lisp package ilu.

Names from interface specifications are transformed into Lisp names (case-insensitive) by inserting hyphens at lower-to-upper case transitions. Hyphens that are already present are maintained as is.(6)

A separate package is defined for each interface with defpackage. The name of this package is taken from the name of the interface. This package uses the packages common-lisp and ilu. The Common Lisp names of all entities defined in the ISL are exported from the package, including types, classes, constants, accessors, type predicates, generic functions, exceptions, etc. Such symbols are also shadowed, to avoid conflicts with used packages. For example, given the following interface:

INTERFACE MyInterface END;
EXCEPTION TotalLoser : Person;
TYPE Person = OBJECT
  METHODS
     FriendsP (someone : Person) : Boolean
       RAISES TotalLoser END
  END;

the stubber generates the following defpackage:

(defpackage :my-interface
  (:use :common-lisp :ilu)
  (:shadow #:total-loser #:person #:friends-p)
  (:export #:total-loser #:person #:friends-p))

This allows symbols defined in the commonlisp package to be used by the automatically generated code in the generated package, but it also means that the user needs to be careful about using any generated package. In general, we recommend that you explicitly specify the full name of symbols from ILU interfaces.

Types

ILU types appear in Common Lisp as follows:

  1. Exceptions are represented with CL conditions, defined by define-condition. All ILU conditions are subtypes of ilu:rpc-exception, which is a serious-condition. If a value is specified for an exception it may be accessed with ilu:exception-value on the condition signalled.
  2. Constants are implemented in CL by a value of the appropriate type, defined with defconstant.
  3. Type aliases (one type name specified as another) are implemented with deftype.
  4. Sequences are implemented as lists, except for sequences of characters, which are implemented as simple-strings.
  5. BOOLEAN values are represented as normal Common Lisp logical values, typically either commonlisp:t or commonlisp:nil.
  6. Arrays and sequences of CHARACTER (regular or SHORT) are implemented as simple-strings.
  7. Arrays of bytes are implemented with objects of type (simple-array (unsigned-byte 8) (*)).
  8. Other arrays are implemented as simple-arrays.
  9. Unions are implemented as with
    (deftype lisht () `(or knul kons)). (7)
  10. Enumerations with keywords, as in
    (deftype answer () `(member :yes :no :maybe))
  11. Record types are implemented with CL defstruct.
  12. Classes are implemented with CLOS defclass.

    Private slots are created for methods which are specified as functional, and the runtime caches the value of this method in such slots after the first call to the method.

    Methods always take as their first argument the object which they are a method on. Subsequent arguments are those specified in the `.isl' file. Methods that have OUT or INOUT arguments may return multiple values. In general, the parameters to a method are the IN and INOUT parameters specified in the ISL interface, but not the OUT parameters. The return values from a method are the specified return value for the ISL method, if any, followed by the INOUT and OUT parameters for the method, if any, in the order in which they appear in the ISL specification of the method.

Using a Module from Common Lisp

To use a module from Common Lisp, you must first have loaded the PDEFSYS "system" that describes the module. Typically, for an ILU interface called Foo, the system can be loaded by invoking
(pdefsys:load-system :foo)
Next, you must bind an instance of an object from that interface. The most common way of doing this is to receive an instance of an object from a method called on another object. But to get the first object exported by that module, one can use either ilu:sbh->instance or ilu:lookup.

Function: ilu:sbh->instance (PUTATIVE-TYPE-NAME symbol) (SBH string) &optional (MOST-SPECIFIC-TYPE-ID simple-string mstid of specified PUTATIVE-TYPE) => ilu:rpc-object

Accepts an ILU string binding handle and Common Lisp type name, and attempts to locally bind an instance of that type with the OID specified in the string binding handle. If no such instance exists locally, a surrogate instance is created and returned. If a true instance exists locally, that instance will be returned.

Function: ilu:lookup (PUTATIVE-TYPE-NAME symbol) (OID simple-string) => (or nil ilu:rpc-object)

This routine will find and return an object with the OID OID, if such an object has been registered in the local domain via the ILU simple binding protocol. The simple binding protocol is experimental in release 1.6 of ILU, and may change without warning in later releases. See section Publishing, for an example.

Various ILU attributes of an object type may be discovered at run time with the generic function ilu:ilu-class-info.

Function: ilu:ilu-class-info (DISC (or ilu:ilu-object type-name)) (WHAT keyword) => (or string boolean list)

This routine will return the specified piece of information about the ILU class specified with DISC, which may be either a CLOS class name, or an instance of the class, and with WHAT, which identifies which piece of information to return. WHAT may have the following values:

Implementing a Module in Common Lisp

For each ILU class interface.otype, ILU will define, in the file `interface-server-procs.lisp', a CLOS class called interface:otype.IMPL. To implement a true object for interface.otype, one should further subclass this CLOS class, and override all of its methods. In particular, do not let any of the default methods for the class be called from your methods for it.

ILU supports, in each address space, multiple instances of something called a kernel server, each of which in turn supports some set of object instances. A kernel server exports its objects by making them available to other modules. It may do so via one or more ports, which are abstractly a tuple of (rpc protocol, transport type, transport address). For example, a typical port might provide access to a kernel server's objects via (Sun RPC, TCP/IP, UNIX port 2076). Another port on the same kernel server might provide access to the objects via (Xerox Courier, XNS SPP, XNS port 1394).

When creating an instance of a true object, a kernel server for it, and an instance id (the name by which the kernel server knows it) for it must be determined. These may be specified explicitly by use of the keyword arguments to commonlisp:make-instance :ilu-kernel-server and :ilu-instance-handle, respectively. If they are not specified explicitly, the variable ilu:*default-server* will be bound, and its value will be used; a default instance handle, unique relative to the kernel server, will be generated.

A kernel server may be created by instantiating the class ilu:kernel-server. The keyword argument :id may be specified to select a name for the server. Note that ILU object IDs, which consist of the kernel server ID, plus the instance handle of the object on that server, must be unique "across space and time", as the saying goes. If no kernel server id is specified, ILU will generate one automatically, using an algorithm that provides a high probability of uniqueness. If you explicitly specify a kernel server ID, a good technique is to use a prefix or suffix which uniquely identifies some domain in which you can assure the uniqueness of the remaining part of the ID. For example, when using ILU at some project called NIFTY at some internet site in the IP domain department.company.com, one might use kernel server IDs with names like something.NIFTY.department.company.com.

=> (make-instance 'ilu:kernel-server :id "FOO-SERVER-1")
#<ILU:KERNEL-SERVER "FOO-SERVER-1">
=> (make-instance 'ilu:kernel-server)
#<ILU:KERNEL-SERVER "121.2.100.231.1404.2c7577eb.3e5a28f">
=>

To export a module for use by other modules, simply instantiate one or more instances of your subtypes of interface:otype.IMPL.

=> (make-instance 'foo:my-bar.impl :ilu-server s)
#<FOO:MY-BAR.IMPL 0x3b32e8 "1">
=>

The simplest Common Lisp "server" code would look something like:

(defun start-server ()
  (make-instance 'foo:my-bar.impl))

which will create an instance of FOO:MY-BAR.IMPL and export it via a default server.

Publishing

To enable users of your module find the exported objects, you may register the string binding handle of the object or objects, along with their type IDs, in any name service or registry that is convenient for you. In release 1.6 of ILU, we are supporting an experimental simple binding method that allows you to "publish" an object, which registers it in a domain-wide registry, and then to withdraw the object, if necessary. Potential clients can find the string binding handle and type ID of the object by calling a lookup function. Note that this interface and service is experimental, and may be supported differently in future releases of the ILU system.

Function: ilu:publish (OBJ ilu:rpc-object) => boolean

Accepts an ilu:rpc-object instance and registers it with some domain-wide registration service. The object is known by its object ID (OID), which is composed of the ID of its kernel server, plus a server-relative instance ID, typically composed as instance-ID@server-ID. Clients may find the object by looking up the OID via the ilu:lookup function. The function returns non-cl:nil if the publication succeeded.

Function: ilu:withdraw (OBJ ilu:rpc-object) => boolean

If OBJ is registered, and if it was published by the same address space that is calling withdraw, its registration is withdrawn. The function returns non-cl:nil if the object is no longer published.

If you wanted to create an instance, and publish it, the code for starting a service might look something like this:

(defun start-server ()
  (let* ((ks (make-instance 'ilu:kernel-server
                ;; specify the service id
                :id "service.localdomain.company.com"))
         (si (make-instance 'foo:my-bar.impl
                ;; specify the server
                :ilu-kernel-server ks
                ;; specify the instance handle
                :ilu-instance-handle "theServer")))
    ;; the OID for "si" is now "theServer@service.localdomain.company.com"
    (ilu:publish si)
    si))

Someone who wanted to use this service could then find it with the following:

(defun find-server ()
  (ilu:lookup 'foo:bar "theServer@service.localdomain.company.com"))

Debugging

To help with finding errors in your methods, the variable *debug-uncaught-conditions* is provided.

Variable: ilu:*debug-uncaught-conditions*

If cl:t, causes a server to invoke the debugger when an unhandled error in user code is encountered, rather than the default action of signalling an exception back to the caller. The default value is cl:nil.

Dumping an image with ILU

ILU has dynamic runtime state. In particular, after it is initialized, it uses several Common Lisp threads to maintain part of its state, and may also keep open connections on operating system communication interfaces. If you wish to dump an image containing ILU, you must dump the image before initializing the ILU module.

Initialization occurs automatically whenever a instance of ilu:ilu-object or ilu:rpc-server is created. Thus you should not create any instances of either true or surrogate ILU objects before dumping the image. However, you may load all the interface code for any interfaces that you are using, before dumping the image.

Initialization may also be accomplished by an explicit call to ilu:initialize-ilu:

Function: ilu:initialize-ilu

Initializes the ILU module.

You may check to see whether the system has been initialized by examining the variable ilu::*ilu-initialized*, which is t iff ilu:initialize-ilu has been invoked.

The Portable DEFSYSTEM Module

ILU support uses a portable implementation of DEFSYSTEM to specify modules to Common Lisp. See section The ILU Common Lisp Portable DEFSYSTEM Module, for details of this system.

ILU Common Lisp Lightweight Processes

ILU currently assumes the existence of lightweight process, or thread, support in your Common Lisp implementation. It uses these internally via a generic veneer, described fully in section The ILU Common Lisp Lightweight Process System.

Go to the previous, next section.