Go to the previous, next section.

Using ILU with Standard C

C Mapping Introduction

This document is for the C programmer who wishes to use ILU. (By C, we mean the language defined in the ISO/ANSI standard, not `K&R C', or `Portable C'.) The following sections will show how ILU is mapped into C constructs and how both C clients and servers are generated and built.

Using ILU with C is intended to be compatible with the OMG CORBA specification. That is, all of the naming and stub generation comply with the Common Object Request Broker Architecture, revision 2.0. (6)

Note that ILU does not support non-ANSI variants of the C language. In particular, it relies on having prototypes, all C library functions, and the capabilities of the C pre-processor.

When functions are described in this section, they are sometimes accompanied by locking comments, which describe the locking invariants maintained by ILU on a threaded system. See the file `ILUHOME/include/iluxport.h' for more information on this locking scheme, and the types of locking comments used.

A number of macros are used in function descriptions, to indicated optional arguments, and ownership of potentially malloc'ed objects. The macro OPTIONAL(type) means that the value is either of the type indicated by type, or the value NULL. This macro may only be used with pointer types. The macro RETAIN(type) indicates, when used on a parameter, that the caller retains ownership of the value, and when used in the result position, that the called function retains ownership of the value. The macro PASS(type) indicates, when used on a parameter, that the caller is passing ownership of the storage to the called function, and when used in the result position, that the called function is passing ownership of the called value to the caller. The macro GLOBAL(type) means that neither the caller nor the calling function owns the storage.

The ISL Mapping to C

Names

In general, ILU constructs C names from ISL names by replacing hyphens with underscores. Type names and class names are prepended with their interface name. For example, for the ISL type T-1 in interface I, the generated name of the C type would be I_T_1.

Method name prefixes are specified by CORBA to be module-name_interface-name. C function names for ISL methods are composed of the generated class name prepended to the method name. For example, if the interface name is X and the class type name is Y and the ISL method name is Z then the C callable method name will be X_Y_Z. ILU C servers for this method must implement a function called server_X_Y_Z.

For field names within records and unions, hyphens are replaced with underscores.

Interface

The ISL interface is mapped to a prefix for all generated type names, constant names, and exception names, by replacing all hyphens in the interface name with underscore characters.

Basic Types

The following basic ISL types have the corresponding mappings in C, as specified by the CORBA 2.0 standard mapping for C:

Constants

ISL constants are translated to C const expressions initialized to the specified value. Constant names are prepended with their interface name, separated from the name of the constant with a hyphen character.

Strings and Characters

ISL character and short character types are represented with the ILU types ilu_character, which hold values of 16-bit Unicode, and ilu_shortcharacter, which hold values of 8-bit ISO Latin-1.

String sequences (SEQUENCE OF SHORT CHARACTER or SEQUENCE OF CHARACTER) are just arrays of the character codes for the characters, using either Latin-1 codes (for SEQUENCE OF SHORT CHARACTER), or ISO 10646 Unicode codes (for SEQUENCE OF CHARACTER). These sequences are terminated with a character code of zero. The terminating code is not counted in the length of the sequence.

Pickles and Typecodes

ILU pickles are mapped to opaque structures of the type CORBA_any, as per the CORBA specification for the type any. However, in ILU, the fields of the pickle are not directly accessible. Instead, the following utility functions are provided to manipulate pickles:

Function: PASS(CORBA_any *) ILU_C_Any_Create (RETAIN(CORBA_TypeCode) typecode, RETAIN(void *) value, RETAIN(CORBA_Environment *) env)

Locking: n/a

Create a new pickle from a C value and typecode. The return value is heap-allocated.

Function: PASS(CORBA_any *) ILU_C_Any_Init (RETAIN(CORBA_any *) uninitialized-any, RETAIN(CORBA_TypeCode) typecode, RETAIN(void *) value, RETAIN(CORBA_Environment *) env)

Locking: n/a

Given a pointer to an uninitialized CORBA_any value uninitialized-any, sets the typecode and value of the any to the specified typecode and value.

Function: PASS(CORBA_any *) ILU_C_Any_ResetValue (RETAIN(CORBA_any *) initialized-any, RETAIN(CORBA_TypeCode) typecode, RETAIN(void *) value, RETAIN(CORBA_Environment *) env)

Locking: n/a

Given a pointer to a previously used CORBA_any value initialized-any, frees the current value and sets the typecode and value of the any to the specified typecode and value.

Function: PASS(void *) ILU_C_Any_Value ({RETAIN(CORBA_any *)} pickle, {RETAIN(CORBA_Environment *)} env)

Locking: n/a

Returns the C value from the pickle. Returns NIL if the type of the value contained in the pickle is not `known' to the ILU C runtime. Relatively expensive, as it involves several malloc's.

Function: CORBA_TypeCode ILU_C_Any_TypeCode ({RETAIN(CORBA_any *)} pickle, {RETAIN(CORBA_Environment *)} env)

Locking: n/a

Retrieve the CORBA typecode of the value in the pickle. Returns NIL if the type of the value in the pickle is not registered with the ILU C runtime.

Function: PASS(CORBA_any *) ILU_C_Any_Duplicate (RETAIN(CORBA_any *) pickle, RETAIN(CORBA_Environment *) env)

Locking: n/a

Make a copy of an existing pickle without `looking inside'. This call will work even with pickle values that are of types not known to the ILU C runtime.

Constructed Types

Enumeration

ISL enumeration types are mapped C enum types, in an exception to the CORBA specification. Each element of the enumeration is named as <interface>_<element-name>. The C enum type is typedef'ed to the specified name for the type. For example, the ISL definition

INTERFACE Foo;
...
TYPE Color = ENUMERATION Red, Green, Blue END;
...
would produce the following C definition:
typedef enum { Foo_Red, Foo_Green, Foo_Blue } Foo_Color;

Array

Arrays are represented as C arrays.

Sequence

Sequence type names, as most type definitions, are formed with the interface name and the type name. Sequence instances are represented to the C programmer as a pointer to the sequence descriptor structure. For each sequence type declared in the interface description, a pseudo-object sequence type is defined in C. These sequence types will hold any number of values of type sequence's primary type. For the sequence
INTERFACE I;
TYPE T2 = SEQUENCE OF T1;
the following functions are defined:

Sequence Method: PASS(I_T2*) I_T2_Create ( OPTIONAL(unsigned long) length, OPTIONAL(T1 *) initial-values )

This function creates and returns a pointer to a newly allocated instance of T2. If length is specified, but initial-values is not specified, enough space for length values of type T1 is allocated in the sequence. If initial-values is specified, length is assumed to be the number of values pointed to by initial-values, and must be specified. Note that if type T1 is a character or short character type, a pointer to a NIL-terminated sequence will be returned; otherwise, a normal CORBA sequence structure will be returned by reference.

Sequence Method: CORBA_unsigned_long I_T2_Length ( I_T2 * s )

Returns the length of s.

Sequence Method: void I_T2_Append ( I_T2 * s, T1 value )

Appends value to the end of s. This function will reallocate space and copy, if necessary.

Sequence Method: void I_T2_Push ( I_T2 * s, T1 value )

Pushes value on to the beginning of the sequence. This function will reallocate space and copy, if necessary.

Sequence Method: void I_T2_Pop ( I_T2 * s, T1 * value-ptr )

Removes the first value from the sequence s, and places it in the location pointed to by value-ptr.

Sequence Method: void I_T2_Every ( I_T2 * s, void (*func)(T1, void *), void * data )

Calls the function func on each element of s in sequence, passing data as the second argument to func.

Sequence Method: RETAIN(I_T1*) I_T2_Nth ( I_T2 * s, CORBA_unsigned_long n )

Returns the address of the nth element of the sequence s. Returns ILU_NIL if n is out of range.

Sequence Method: void I_T2_Init ( I_T2 * s, OPTIONAL(CORBA_unsigned_long) length, OPTIONAL(T1 *) initial-values )

This function works like T2_Create, except that it takes a the address of an already-existing T2 to initialize. This can be used to initialize instances of T2 that have been stack-allocated.

Sequence Method: void I_T2__Free ( I_T2 * s )

Frees allocated storage used internally by s. Does not free s itself.

String sequences (SEQUENCE OF SHORT CHARACTER or SEQUENCE OF CHARACTER) are just arrays of the character codes for the characters, using either Latin-1 codes (for SEQUENCE OF SHORT CHARACTER), or ISO 10646 Unicode codes (for SEQUENCE OF CHARACTER). These sequences are terminated with a character code of zero. The terminating code is not counted in the length of the sequence. All other sequence types have a record structure, mandated by CORBA:

typedef struct I_T2 {
  unsigned long _maximum;
  unsigned long _length;
  I_T1 *_buffer;
} I_T2;

The field _maximum contains the number of elements pointed to by _buffer. The field _length indicates the number of valid or useful elements pointed to by _buffer.

For example, the ISL specification

INTERFACE I;
 
TYPE iseq = SEQUENCE OF INTEGER;
would have in its C mapping the type
typedef struct I_iseq {
  unsigned long _maximum;
  unsigned long _length;
  ilu_integer *_buffer;
} I_iseq;
In a client program, a pointer to this type would be instantiated and initialized by calling the type specific sequence creation function generated for the sequence, e.g.
        ...
    I_O h;
    ILU_C_ENVIRONMENT s;
    I_iseq sq;
        ... 
    sq = I_iseq_Create (0, NULL);
    I_iseq_Append (&sq, 4);
        ...

Record

Records map directly into corresponding C structures.

Union

Because of the somewhat baroque CORBA concept of union types, unions may take one of several forms.

Generally, ILU unions in C consist of a struct with two members: the type discriminator (a member named "_d"), and a union (a member named "_u") of the possible values. In a simple ISL union that does not name the elements, the union member names are derived from the ISL data types which compose the union. For example, if the ISL type in interface I is TYPE u1 = UNION INTEGER, SHORT REAL END; the generated C struct would be

struct _I_u1_union {
        CORBA_short _d;
        union {
                CORBA_long integer;     /* 0 */
                CORBA_float shortreal;  /* 1 */
        } _u;
};
typedef struct _I_u1_union I_u1;

Note the discriminator _d may take on the values of 0, for the integer field, or 1, for the shortreal field.

In more complex union forms, the user may specify the type of the discriminator as well as the member names and which member corresponds to which discriminator value. Consider the following ISL example:

INTERFACE I;
TYPE e1 = ENUMERATION red, blue, green, yellow, orange END;
TYPE u1 = e1 UNION 
 a : INTEGER = red, green END,
 b : SHORT REAL = blue END,
 c : REAL = DEFAULT
END;

The generated union is:

typedef struct _I_u1_union I_u1;
typedef enum { I_red = 0, I_blue = 1, I_green = 2, I_yellow = 3, I_orange = 4 } I_e1;
struct _I_u1_union {
  I_e1 _d;
  union {
    CORBA_long a;       /* I_red, I_green */
    CORBA_float b;      /* I_blue */
    CORBA_double c;     /* DEFAULT */
  } _u;
};

This example shows that the discriminator type is to be I_e1 and that the member names are to be a, b, and c. When the discriminator has the value I_red or I_green the member a has a valid value and the type is interpreted to be CORBA_long. When the discriminator has the value I_green the member b has a valid value and the type is interpreted to be CORBA_float. If the discriminator has any other value, the member c is expected to have a valid value and the type is interpreted to be CORBA_double.

Discriminator types may be INTEGER, CARDINAL, ENUMERATION, SHORT CARDINAL, or SHORT INTEGER. The default for an unspecified discriminator is SHORT INTEGER.

Optional

An ISL OPTIONAL type maps either to the same C type as its base type, if that base type is represented with an C pointer type, or to a pointer to that base type, if it is not represented with a C pointer type.

Object Types

Surrogate and True Objects

ILU uses an object system embedded into C. The C type of objects in this system is ILU_C_Object*; the identifiers ILU_C_OBJECT and CORBA_Object can also be used for this type. We recommend the use of ILU_C_OBJECT, as it is a macro that expands to the identifier for the C object type specified by the version of CORBA being used (currently 2.0).

Since C has no subtyping relationship isomorphic to that of ISL object types, a more liberal approximation is used: all ISL object types map to the same C type, CORBA_Object. However, a typedef is emitted for each ISL object type, so an object-type-specific C type name may be used to express intent. For conformance with CORBA, the C name for ISL type I.T is I_T.

The C mapping of an object type includes a set of generic functions for the methods introduced at that object type. The name of the generic function for the method M introduced at object type I.T is I_T_M. This generic function is used to invoke method M on objects of type I.T (naturally including objects that also have types that are subtypes of I.T). The first argument to a generic function is an object instance (ILU_C_Object *) that should have type I.T (among possibly others).

An implementation of an ISL object type is commonly referred to as a class. Classes are represented in the ILU C runtime as the C type ILU_C_Class. To create an ILU_C_Class, the application calls an object-type-specific function that is part of the stubs. The class-creation procedure named I_T__MakeClass, which is declared in generated file `I.h', makes a class for objects that implement object type I.T (and thus all its supertypes). This procedure takes as arguments a set of C procedures that are the implementations of the methods of that class, plus a finalization procedure. The finalization procedure is given access to the private data of the object after the object is destroyed.

For each object type I.T, the generated server-side stub module for I creates a default true class, unless the true code has been generated with the -nodefaulttrueclass or -nodefaulttrueclassfor command-line switches. Linking with this server-side stub requires the application to supply the procedures that implement the methods of this default class. Those procedures are named server_I_T_M, for each method M of I.T. A finalization procedure can be associated with the default class by invoking I_T__SetDefaultClassFinalization. Implementations of true objects typically just use this default class, since the methods for this class have to be provided in any case. The function I_T__CreateTrue will create an true instance using the default true class for the object type I_T. I_T__CreateTrue methods take an instance handle, a server on which to maintain the object, and arbitrary user data, and create and return the true instance of the object. An alternate version of I_T__CreateTrue, called I_T__OTCreateTrue, is provided for use inside the scope of an object table's incarnation procedure. Also, a generic creation procedure, ILU_C_CreateTrueObject, is declared in `iluchdrs.h' for application use. The default true class can be registered or changed with the function I_T__SetDefaultClass, which returns the previous setting of the default true class.

Surrogate instances generally use another automatically-constructed class, though custom surrogate classes may be registered with the procedure ILU_C_RegisterCustomSurrogateType. Surrogate instances are typically either received as reply values from calls or parameters to calls, or reified from one or more strings with a binding procedure. (A binding procedure is a procedure that takes some name for an object instance, and returns the actual instance.) There are a number of binding procedures available. The simple binding interface to C offers the function ILU_C_LookupObject, which takes an object ID and a type, and returns the registered object with that ID, if any. The function CORBA_ORB_string_to_object will take a URL for an instance, in any of the supported URL forms, and return an ILU_C_OBJECT instance. The function ILU_C_SBHToObject is similar to CORBA_ORB_string_to_object, except that an expected object type may also be specified, to constrain the process. In addition, the ILU-generated stubs will provide a function called I_T__CreateFromSBH, which will either find or create an instance of the specified type, with the specified parameters.

In general, for any object type T, the following C functions are defined:

C Procedure Type: ILU_C_FinalizationProc

Locking: caller may have any locks -- this means that this finalization procedure can do almost nothing ILU-related without potentially violating locking constraints

The finalization procedure for a class of objects. It takes a single parameter, the void * "instance data" pointer provided at the creation of the object being finalized. Ownership of (instanceData) is passed to this procedure. It has a return type of void.

Function: ILU_C_Class T__MakeClass ( method-1-type method-1-proc, ... method-N-type method-N-proc, ILU_C_FinalizationProc _finalize )

Locking: Main Invariant holds

Creates a C class of objects that export object type T (and all its supertypes), given implementations for all the methods of that type. ILU_C_CreateTrueObject can then be called to create instances of this class.

Function: ILU_C_Class T__SetDefaultClass ( ILU_C_Class c )

Locking: Main Invariant holds

Sets the default true class of the type T to be c, and returns the previous default true class setting.

Function: void T__SetDefaultClassFinalization ( ILU_C_FinalizationProc f )

Locking: Main Invariant holds

Sets the finalization method of the default class for object type T.

Function: OPTIONAL(T) T__CreateTrue ( OPTIONAL(RETAIN(char *)) instance-id, OPTIONAL(GLOBAL(ilu_Server)) server, OPTIONAL(PASS(void *)) user-data )

Locking: Main Invariant holds

Creates an instance of the default class for type T, exporting it with instance-id instance-id, exporting it via server server, associating the value user-data with it. If instance-id is not specified, a server-relative instance-id will be assigned automatically. If server is not specified, a default server will be created automatically.

Function: OPTIONAL(T) T__OTCreateTrue ( RETAIN(char *) instance-id, GLOBAL(ilu_Server) server, OPTIONAL(PASS(void *)) user-data )

Locking: Inside(server, T)

Similar to T__CreateTrue(), but designed to be used within the ot_object_of_ih function of an object table (section Servers and Ports). Requires kernel server locks to be held before invocation.

Creates an instance of the default class for type T, exporting it with instance-id instance-id, exporting it via server server, associating the value user-data with it.

Function: OPTIONAL(T) T__CreateFromSBH ( RETAIN(char *) sbh, RETAIN(CORBA_Environment *) Env)

Locking: Main Invariant holds

Finds or creates an instance of T, using the given object reference.

Class Var: extern ilu_Class T__MSType

A value, of type ilu_Class (which, despite its poorly-chosen name, identifies an object type, not a class), that identifies the object type T.

In the following example, the ILU definition is:

INTERFACE I;
 
TYPE T = OBJECT
  METHODS
    M ( r : REAL ) : INTEGER
  END;

This definition defines an interface I, an object type T, and a method M. The method M takes a REAL as an argument and returns an INTEGER result. The generated C header file would include the following statements:

typedef ILU_C_OBJECT I_T;

ILU_C_Class I_T__MakeClass(
  ilu_integer (*I_T_M__Impl)
     (I_T _handle, ilu_real r, ILU_C_ENVIRONMENT *_status),
  ILU_C_FinalizationProc _finalize);

I_T I_T__CreateFromSBH (char *sbh, ILU_C_ENVIRONMENT *Env);
I_T I_T__CreateTrue (ilu_string ih, ilu_Server server, void *user_data);
I_T I_T__OTCreateTrue (ilu_string ih, ilu_Server server, void *user_data);

ilu_integer I_T_M (I_T, ilu_real, ILU_C_ENVIRONMENT *);

ilu_integer server_I_T_M (I_T, ilu_real, ILU_C_ENVIRONMENT *);

The functions I_T__CreateFromSBH, I_T__CreateTrue, and I_T__OTCreateTrue are used to create instances of the class I_T. I_T__CreateFromSBH is used by clients. I_T__CreateTrue is used by servers for normal circumstances, and I_T__OTCreateTrue is used in object table implementations; both return objects whose M method is implemented by server_I_T_M. Alternatively, servers and/or object tables could use I_T__MakeClass and ILU_C_CreateTrueObject. The pointer returned in each case is the object instance and must be passed with each method invocation.

Interface Inheritance

Through interface inheritance, an object type may participate in the behaviors of several different types that it inherits from. These types are called ancestors of the object type. In C, an object type supplies all methods either defined directly on that type, or on any of its ancestor types.

Consider the following example:

INTERFACE I2;
 
EXCEPTION E1;

TYPE T1 = OBJECT
  METHODS
    M1 (a : ilu.CString) : REAL RAISES E1 END
  END;

TYPE T2 = OBJECT
  METHODS
    M2 ( a : INTEGER, Out b : INTEGER )
  END;
 
TYPE T3 = OBJECT SUPERTYPES T1, T2 END
  METHODS
    M3 ( a : INTEGER )
  END;

The object type T3 inherits from the object types T1 and T2. Thus, eight C procedures are relevant to the interface I2: the three generic functions I2_T1_M1, I2_T2_M2, and I2_T3_M3, and the five default method implementations server_I2_T1_M1, server_I2_T2_M2, server_I2_T3_M1, server_I2_T3_M2, and server_I2_T3_M3. A module that implements true instances of T3 using the default class would define the last three procedures (the other two default method implementations, for messy reasons described in the next paragraph). A client uses only the three generic functions.

Sadly, the current state of the C-stubber causes an additional complexity for server implementors. `I2-true.c' contains the server-side stubs ("skeletons", in OMG parlance) needed in any program that implements any object type that is a subtype of any object type defined in `I2.isl'. `I2-true.c' also contains the code that creates the default classes for all the object types defined in `I2.isl'; this code makes external references to the default implementation procedures, thus requiring any program that links with `I2-true.o' to supply those default implementation procedures --- even if those default classes are not used. A simple workaround is to supply dummy procedures to satisfy the linker. The stubs can also be generated with the command-line options -nodefaulttrueclass or -nodefaulttrueclassfor, which will prevent generation of the code that creates the default true classes. However, if this technique is used, be aware that either a default true class must be registered manually, or true instances must be created with ILU_C_CreateTrueObject.

Accessing the String Binding Handle, IOR, or Object ID

Several functions are provided to give access to various identifiers of an instance. The function ILU_C_SBHOfObject will return the ILU URL for an instance; the function ILU_C_IOROfObject will return the CORBA URL for an object, if support for IIOP is configured into ILU; the function CORBA_ORB_object_to_string will return either the IOR, if IIOP support has been configured in, or the SBH, if not; the two parts of the object ID, the server id and the instance handle, may be obtained with a call on ILU_C_IDOfObject. See the API reference section of this chapter for more information on these functions.

Distinguishing Between Local and Remote Instances

It is occasionally useful to distinguish between local and remote instances. There are three cases here: the case where the instance is a true object, the case where the instance is a surrogate for a true instance implemented in another language in the same address space, and the case where the instance is a surrogate for a true instance in a different address space. There is currently no good way to distinguish these cases in the C runtime.

Object Implementation

This information is provided for those interested in the implementation of the C object system. It is not guaranteed to remain the same from release to release.

The interface `ILUSRC/runtime/c/ilucstub.h' contains the C declarations relevant to the material here.

A C class is represented by a pointer to a struct that holds a finalization procedure and a dispatch table. The dispatch table is an array of sections, one from each object type implemented by the class. Each section is a struct containing the ilu_Class (remember, this identifies a type, not a class) that this section is for, and an array of procedure pointers, one per method introduced at that object type.

For each method directly defined in the type, a generic function is defined in the common code for its interface, which dispatches to the appropriate method. It does this by walking down the dispatch table for the object's class, until it finds a section that contains the appropriate ilu_Class value (that is, the value of ilu_Class that matches the ilu_Class at which this method was introduced), then calling the method pointer which is indexed in the section's array of method pointers by the index of the method. The generic functions have the correct type signature for the method. They can be referenced with the & operator.

Methods

All ISL methods of an object type map to C functions which operate on instances of the C object system as described above. IN, OUT, and INOUT parameters appear in the C function in the same order as they appear in the ISL definition of the function.

ASYNCHRONOUS methods have no return values and raise no user-specified exceptions. They may return before the completion of the true method. FUNCTIONAL methods are not cached by the C ILU runtime.

In addition to its specified arguments, the methods I_T_M and server_I_T_M take an instance of the type I_T and a reference to a variable of type ILU_C_ENVIRONMENT *, which is a macro defined to be the appropriate CORBA environment type, and is used to return exception codes. The environment struct pointed to by the environment argument must be instantiated in a client; its address is passed as the last argument to each method. True procedures must expect a pointer to this structure as the last argument. For instance, the C client calling the method for M might be as follows:

#include "I.h"
 
int main (int ac, char **av)
{
  double atof( );
  I_T inst;
  CORBA_long result;
  CORBA_double f;
  ILU_C_ENVIRONMENT ev;
 
  I__Initialize( );
  f = atof (av[1]);
  inst = I_T__CreateFromSBH (av[2], &ev);
  if (!ILU_C_SUCCESSFUL(&ev)) {
    printf( "CreateFromSBH raised exception <%s>\n",
      ILU_C_EXCEPTION_ID(&ev));
    return(1);
  }
  result = I_T_M (inst, f, &ev);
  if (!ILU_C_SUCCESSFUL(&ev)) {
    printf( "exception <%s> signalled on call to I_T_M\n",
      ILU_C_EXCEPTION_ID(&ev));
    return(2);
  }
  printf( "result is %d\n", result );
  return(0);
}

Note the call on the interface-specific client initialization procdedure I__Initialize; these are described in a later section.

The string binding handle is obtained from standard input along with some floating-point value. The class specific function I_T__CreateFromSBH is then called to obtain the object instance. This function was passed the string binding handle, and a CORBA_Environment in which to report exceptions. The returned object instance is then passed as the first argument to the method I_T_M, along with the environment ev, and the single actual CORBA_double argument f. I_T_M returns an CORBA_long value which is placed in result.

The true implementation of the method M might use the default class, supplying the implementation of the one method as follows:

ilu_integer server_I_T_M ( I_T h, ilu_real u, ILU_C_ENVIRONMENT *s )
{
  return( (ilu_integer) (u + 1) );
}

In this simple example, the corresponding server, or true, method computes some value to be returned. In this case it adds one to its ilu_real argument u, converts the value to an integer, and returns that value. Note that the server method, if not signalling any exceptions, may ignore the environment parameter.

Parameter Passing Considerations

Here is ILU's version of table 20 from the CORBA 2.0 spec. T is the C mapping of the type in question. The Exn column describes how exception parameters appear in the parameter-conveying member of a status struct.

DataType        In      InOut   Out        Return    Exn
--------        --      -----   --        ------    ---
scalar          T       T*      T*         T         T*
optional        T       T*      T*         T         T*
object          T       T*      T*         T         T*
record, fixed   T*      T*      T*         T         T*
record, var     T*      T*      T**        T*        T*
union, fixed    T*      T*      T*         T         T*
union, var      T*      T*      T**        T*        T*
string          T       T*      T*         T         T*
other sequence  T*      T*      T**        T*        T*
array, fixed    T       T       T          T_slice*  T*
array, var      T       T       T_slice**  T_slice*  T*

Exceptions

C has no defined exception mechanism. As already indicated, exceptions are passed in ILU C by adding to the end of each method an additional status argument that can convey an exception code and a value of a type associated with that exception. To signal an exception, a method implementation sets the exception code and supplies the parameter value (if any).

An exception parameter is conveyed in the status argument as a C pointer; the parameter-conveying member is declared to be a void *. In particular, this pointer is a pointer to a value of the type that is the C mapping of the exception's ISL parameter. For an exception that has no parameter, the parameter-conveying member is not meaningful.

In the following example, the div method can raise the exception DivideByZero:

INTERFACE calc;
 
TYPE numerator = INTEGER;
 
EXCEPTION DivideByZero : numerator;
 
TYPE self = OBJECT
  METHODS
    Div( v1 : INTEGER, v2 : INTEGER ) : INTEGER RAISES DivideByZero END
  END;

The generated include file `calc.h' contains the exception definitions:

#ifndef __calc_h_
#define __calc_h_
/*
** this file was automatically generated for C
** from the interface spec calc.isl.
*/
 
#ifndef __ilu_c_h_
#include "ilu-c.h"
#endif
 
extern ILU_C_ExceptionCode    _calc__Exception_DivideByZero;
#define ex_calc_DivideByZero _calc__Exception_DivideByZero
 
typedef ilu_integer calc_numerator;
typedef calc_numerator calc_DivideByZero;
 
typedef ILU_C_OBJECT calc_self;
 
calc_self calc_self__CreateTrue ( char *id, ilu_Server server,
   void * user_data);
calc_self calc_self__CreateFromSBH ( char * sbh, ILU_C_ENVIRONMENT *Env );

ilu_integer calc_self_Div( calc_self, ilu_integer, ilu_integer,
   ILU_C_ENVIRONMENT *Env );

extern void calc__BindExceptionValue (ILU_C_ENVIRONMENT *, ilu_Exception, ...);

#endif 

The method implementation for Div in the true module must detect the divide-by-zero condition and raise the exception:

long server_calc_self_Div (calc_self h, ilu_integer u, ilu_integer v,
                           ILU_C_ENVIRONMENT *s)
{
  calc_numerator n = 9;

  if ( v == 0 )
    {
      s->_major = ILU_C_USER_EXCEPTION;
      s->returnCode = ex_calc_DivideByZero;
      s->ptr = (void *) malloc(sizeof(calc_numerator));
      *((calc_numerator *) (s->ptr)) = n;
      s->freeRoutine = (void (*) (void *)) 0;
      return( u );
    }
  else
    return( u / v );
}

When freeing the parameter requires more than just freeing s->ptr, a non-NULL s->freeRoutine is provided that does the additional freeing; s->freeRoutine is given one argument, s->ptr, and returns void.

The generated stubs offer as a convenience a variadic procedure (calc__BindExceptionValue) that can be used to raise any exception declared in the interface. For an exception that has no parameter, this procedure takes just two actual arguments. For an exception with a parameter, the parameter value is given as the third actual argument, using the usual calling convention for passing IN arguments of its type. Using this procedure, the above code would be:

long server_calc_self_Div (calc_self h, ilu_integer u, ilu_integer v,
                           ILU_C_ENVIRONMENT *s)
{
  calc_numerator n = 9;

  if ( v == 0 )
    {
      calc__BindExceptionValue(s, ex_calc_DivideByZero, n);
      return( u );
    }
  else
    return( u / v );
}

The exception is sent back to the client, which can detect it thusly:

  ...
  calc_self instance;
  ILU_C_ENVIRONMENT s;
  ilu_integer i, j;
  ilu_integer val;
  ...
  instance = calc_self__CreateFromSBH (sbh, &s);
  
  if (! ILU_C_SUCCESSFUL(&s)) {
    fprintf (stderr, "CreateFromSBH(%s) raised %s\n",
      sbh, ILU_C_EXCEPTION_ID (&s) );
    exit(1);
  }

  val = calc_self_Div (instance, i, j, &s);

  /* check to see if an exception occured */

  if (! ILU_C_SUCCESSFUL(&s)) {
    /* report exception to user */
    char *p;

    p = ILU_C_EXCEPTION_ID (&s);

    if (p == ex_calc_DivideByZero) {
      calc_numerator *ip;
      ip = (calc_numerator *) ILU_C_EXCEPTION_VALUE (&s);
      fprintf (stderr, "%s signaled:  numerator = %d\n", p, *ip);
      }
    else {
      /* odd exception at this point */
      fprintf (stderr, "Unexpected <%s> on call to Div.\n", p);
      }
    /* free up any transient exception data */
    ILU_C_EXCEPTION_FREE (&s);
    }
  else {
    /* no exception - print the result */
    printf( "result is %d \n", val );
    }
  ...

For more complex exception types, it is often helpful to define a procedure in C specifically to raise them.

Garbage Collection of C Instances

Both true and surrogate instances of ILU_C_OBJECT are reference-counted; that is, each instance contains a counter which indicates how many uses are currently being made of that object. When you wish to use an object for another purpose, you should increment the reference count by calling the procedure CORBA_Object_duplicate, which will return a copy of the instance that you can use for the new purpose. When you are finished with an instance, you should call CORBA_Object_release to release your claim on the object's resources. When the reference count of the object returns to zero, the object is finalized.

For COLLECTIBLE true instances, the ILU kernel will maintain a distributed reference count on the instance automatically. That is, so long as any client of the server has a reference to the object, the true instance will not be finalized.

Issues in Mapping OMG IDL to C

The ILU system does not support the OMG IDL type fixed, which is a representation of binary-coded-decimal, or BCD. We have no immediate plans to support it in the future.

Access to Standard ILU Features

Servers and Ports

The C type which represents an ILU kernel server is ILU_C_Server. True servers can be created with the function ILU_C_InitializeServer.

Instances of ILU_C_Server are reference-counted, in the same way that instances of ILU_C_OBJECT are. Call ILU_C_Server_duplicate and ILU_C_Server_release to keep track of your usage of ILU_C_Server values. ILU_C_CloseServer and its relatives perform functions orthogonal to ILU_C_Server_release. The actual server will be destroyed only when there is no reason to maintain it; in addition to the reference counting introduced here, the existence of ILU_C_OBJECT values in that server count as another reason to maintain the server.

A kernel server can export itself via multiple ports. ILU_C_InitializeServer may create one, and ILU_C_AddPort can be called to create more.

Object Tables

It is sometimes useful to have a server create true objects only when they are mentioned by a client's actual invocation of a method on them. This is allowed in ILU by an interface called an object table, associated with a server. An object table contains two functions, one of which creates a new true instance when called with an instance handle, and the other of which frees the object table when the server is destroyed. Object tables are associated with servers when the server is created. A value of type ILU_C_ObjectTable may be created by a call on ILU_C_CreateObjectTable; see the API reference for more information about this function.

Server Relocation Functionality in C

The server relocation functionality (see section Server Relocation) is accessed by ILU_C_SetServerRelocationProc (see section Server Manipulation).

Exception Information Functionality in C

See section Exception Information in C.

Threading and Event Loops

The ILU C runtime allows the use of several different kernel threads packages, application-specific threads packages, or various kinds of event loops. See section Threads and Event Loops for a general discussion of threads and event loops in ILU.

The two macros ILU_C_USE_OS_THREADS and ILU_C_FINISH_MAIN_THREAD (see section Server Manipulation) are provided to allow use of standard Win32 threads, POSIX threads, or Solaris threads. Application-specific threads packages can also be used by explicitly setting the wait tech, lock tech, and main loop via calls on the ILU kernel (see @ref{} for more information on these functions), then instructing the C runtime on how to fork a new thread by calling ILU_C_SetFork.

If no thread technology is specified, ILU's C runtime will operate in a single-threaded (i.e., event loop) manner, using the default ILU main loop. That main loop can also be replaced with an application-specific event loop system if desired; this is often useful when using a toolkit like Xt or Tk. This replacement works by calling the kernel procedure ilu_SetMainLoop. In the case of the C runtime, moreover, several examples of how to set the event loop are provided in the directory `ILUSRC/runtime/mainloop/'. They will have been automatically built into a library and installed into `ILUHOME/lib/' if configured in during the build process. They are useful examples of how to set the event loop to override the ILU default event loop.

For single-threaded operation, the main loop must be invoked. This can be done with either ILU_C_Stoppable_Run, ILU_C_Run, ILU_C_StoppableRun, or ILU_C_FINISH_MAIN_THREAD (see section Server Manipulation -- which is woefully incomplete and out of date).

For multi-threaded operation, no main loop need -- or even really can -- be invoked. Some of the aforementioned "main loop" procedures also "work" in multi-threaded runtimes -- they simply block the calling thread.

Custom Records

Custom record support in the ILU C runtime provides support for replacing the default generated record type R with a different struct type S. S must have a field of type R as its first field.

Custom record support for a particular ISL record type can be specified by calling ILU_C_RegisterCustomRecord() on the CORBA_TypeCode value for the type. Note that this allows you to register a function to be called just before marshalling the value to another address space (among other functions). This `pre-output' function may be called more than once on the same call, if the protocol selected requires sizing of arguments. It should be written so that repeated calls on the same value have no effect.

Custom Surrogates

Custom surrogates allow the user to specify custom surrogate object types which may have additional functionality in terms of caching or other side effects, and have them created instead of the default ILU surrogate object type when an instance is received. This functionality is provided in the C runtime with the function ILU_C_RegisterCustomSurrogateType.

String Binding Handle Formation

When a client program is making a call on an object which is provided via an object table, it is often convenient to allow the client to form an appropriate string binding handle for the object, then instantiate a surrogate instance from that string binding handle. The function ILU_C_FormSBH is provided to aid client programs in forming string binding handles.

Simple Binding

The ILU C runtime provides the standard interface to ILU's simple binding mechanism. The function ILU_C_PublishObject publishes a true instance; ILU_C_WithdrawObject allows it to be withdrawn from the registry. The function ILU_C_LookupObject finds and returns an instance with the specified object ID.

Identities and Passports

See ILU_C_CreatePassport, ILU_C_AddIdentity, and ILU_C_DestroyPassport for managing ilu_Passports.

To pass/receive an ilu_Passport in a call, the caller calls ILU_C_SetPassportContext(pp) to store pp in a special hidden per-thread slot, and ILU_C_SetPassportContext() to retrieve it. pp will remain in that slot until another call to ILU_C_SetPassportContext overwrites it. The callee calls ILU_C_CallerIdentity() in a true method to fetch pp from the slot. If the caller and callee are both written in C, and the true method has been invoked directly from the same thread, the returned value will be whatever passport has been set with ILU_C_SetPassportContext(); otherwise it will be the passport passed by the caller.

Pipelining in C

A pipeline (see section Pipelining) is represented in C by an ILU_C_Pipeline, and is created by calling ILU_C_CreatePipeline. A pipeline is associated with calls via a special hidden per-thread slot; this slot is accessed with ILU_C_SetPipelineContext and ILU_C_GetPipelineContext. When the ILU_C_Pipeline is no longer needed, the client calls ILU_C_ReleasePipeline.

Call Order Preservation in C

A serializer (see section Call Order Preservation) is represented in C by an ILU_C_Serializer, and is created by calling ILU_C_CreateSerializationContext. A serializer is associated with calls via a special hidden per-thread slot; this slot is accessed with ILU_C_SetSerializationContext and ILU_C_GetSerializationContext. When the ILU_C_Serializer is no longer needed, the client calls ILU_C_ReleaseSerializer.

Batching in C

A batcher (see section Batching) is represented in C by an ILU_C_Batcher, and is created by calling ILU_C_CreateBatcher. A batcher is associated with calls via a special hidden per-thread slot; this slot is accessed with ILU_C_SetBatcherContext and ILU_C_GetBatcherContext. The application can call _C_PushBatcher to initiate delivery all of all call messages buffered for a given batcher. When the ILU_C_Batcher is no longer needed, the client calls ILU_C_ReleaseBatcher.

Building C/ILU Applications

Using the ILU C Stubber

To generate C stubs from an ISL file, use the program c-stubber. Four files are generated from the `.isl' file:

Typically, clients of a module never have a need for the `interface-name-true.c' file.

% c-stubber foo.isl
header file for interface foo to ./foo.h...
common code for interface foo to ./foo-common.c...
code for surrogate stubs of interface foo to ./foo-surrogate.c...
code for true stubs of interface foo to ./foo-true.c...
%

Command-line Options

The program c-stubber supports the following options:

If none of -true, -surrogate, -common, -headers is specified, the default action is to produce all of them. However, if any of those switches is explicitly specified, only those specified will be produced.

Tailoring Identifier Names

The option -renames renames-filename may be used with c-stubber to specify particular C names for ISL types.

It is sometimes necessary to have the C names of an ILU interface match some other naming scheme. A mechanism is provided to allow the programmer to specify the names of C language artifacts directly, and thus override the automatic ISL to C name mappings.

To do this, you place a set of synonyms for ISL names in a renames-file, and invoke the c-stubber program with the switch -renames, specifying the name of the renames-file. The lines in the file are of the form

construct ISL-name C-name
where construct is one of method, exception, type, interface, or constant; ISL-name is the name of the construct, expressed either as the simple name, for interface names, the concatenation interface-name.construct-name for exceptions, types, and constants, or interface-name.type-name.method-name for methods; and C-name is the name the construct should have in the generated C code. For example:

# change "foo_r1" to plain "R1"
type foo_r1 r1
# change name of method "m1" to "method1"
method foo_o1_m1 method1

Lines beginning with the `sharp' character `#' are treated as comment lines, and ignored, in the renames-file.

This feature of the c-stubber should be used as little and as carefully as possible, as it can cause confusion for readers of the ISL interface, in trying to follow the C code. It can also create name conflicts between different modules, unless names are carefully chosen.

Using ILU Modules From C

Before manipulating surrogate objects, a client module must first call a runtime initialization procedure Foo__Initialize for each ISL interface Foo that declares object types whose surrogates are to be manipulated. Additionally, server modules must also call server initialization procedures (see previous section). These initialization calls may be made in any order, and each procedure may be called more than once. However, no two calls may be done concurrently (this is an issue only for those using some sort of multi-threading package).

A client of an exported module may obtain an object instance either by calling a method which returns the instance, or by calling TYPE__CreateFromSBH() on the string binding handle of an instance. Once the object instance, which is typically a surrogate instance, but may in fact be a true instance, is held by the client, it can be used simply by making method calls on it, as shown above.

Implementing an ILU Module With C

This section will outline the construction of a true module exported by an address space. For the example, we will demonstrate the calculator interface described above. We will also use the CORBA 2.0 names for standard types and exceptions, to show that it can be done.

First, some runtime initialization of the server stubs must be done. Call Foo__InitializeServer for every ISL interface Foo containing an object type implemented by the address space. Due to a misfeature in the current C support, also call Bar__InitializeServer for every ISL interface Bar containing an object type that is a supertype of one defined in Foo (if you don't, the server will get a runtime fault -- due to calling through a NULL procedure pointer --- when serving a call on an inherited method); this may cause you to have to supply dummy procedures, as explained in section Interface Inheritance. Also call any client initialization procedures needed (see next section). These server and client initialization calls can be made in any order, and each initialization procedure can be called more than once. However, no two calls may be done concurrently (this is an issue only for those using some sort of multi-threading package).

Then we create an instance of calc_self. We then make the string binding handle of the object available by printing it to stdout. Finally the ILU_C_Run procedure is called. This procedure listens for connections and dispatches server methods.

The main program for the server is as follows:

#include "I2.h"
 
CORBA_long
  server_calc_self_Div (calc_self h,
                        CORBA_long u,
                        CORBA_long v,
                        CORBA_Environment *s)
{
  calc_numerator n = 9;

  if ( v == 0 )
    {
      calc__BindExceptionValue(s, ex_calc_DivideByZero, n);
      return( u );
    }
  else
    return( u / v );
}

main (int ac, char **av)
{
  calc_self s;
  char * sbh;
  CORBA_Object the_orb;
  CORBA_Environment ev;

  the_orb = CORBA_ORB_init (&ac, av, "ilu", &ev);
  if (!ILU_C_SUCCESSFUL(&ev)) {
    fprintf (stderr, "Can't initialize ORB; exception <%s>\n", CORBA_exception_id(&ev));
    CORBA_exception_free(&ev);
    exit(1);
  };

  calc__InitializeServer( );

  s = calc_self__CreateTrue (NULL, NULL, NULL);
  if (s == NULL)
    {
      fprintf (stderr, "Unable to create instance of calc_self.\n");
      exit(1);
    }
  else
    {
      sbh = CORBA_ORB_object_to_string (the_orb, s, &ev);
      if (ev._major == CORBA_NO_EXCEPTION)
        {
          printf ("%s\n", sbh);
          ILU_C_Run (); /* enter main loop; hang processing requests */
        }
      else
        {
          fprintf (stderr,
                   "Attempt to obtain sbh of object %p signalled <%s>.\n",
                   s, CORBA_exception_id(&ev));
          CORBA_exception_free(&ev);
          exit(1);
        }
    }
}

Libraries and Linking

For clients of an ILU module, it is only necessary to link with the `interface-name-surrogate.o' and `interface-name-common.o' files generated from the C files generated for the interface or interfaces being used, and with the two libraries `ILUHOME/lib/libilu-c.a' and `ILUHOME/lib/libilu.a' (in this order, as `libilu-c.a' uses functions in `libilu.a').

For implementors of servers, the code for the server-side stubs, in the file `interface-name-true.o' compiled from `interface-name-true.c', and in the file `interface-name-common.o' compiled from `interface-name-common.c', should be included along with the other files and libraries.

ILU C API

In addition to the functions defined by the CORBA mapping, the ILU C mapping provides some other functions, chiefly for type manipulation, object manipulation, and server manipulation. There are also a number of macros provided for compatibility with both versions of CORBA (revision 2.0).

General

Exception: CORBA_string ex_CORBA_InvalidName

Raised by CORBA_ORB_resolve_initial_references to indicate that no binding for the specified service_name is known. The associated value is the service name. Note that this differs from the strict CORBA definition of this exception, which has no associated value.

Function: CORBA_Object CORBA_ORB_init ( {int *} argc, {char **} argv, CORBA_string orb_id, {CORBA_Environment *} env)

Locking: Main Invariant holds

Called to initialize the ILU runtime, and acquire the "orb" object. The return value of this call is used in subsequent calls to other CORBA utility functions.

If the environment variable ILU_COS_NAMING_IOR is bound to a string binding handle for a CosNaming service, this call will bind the service name "NameService" to the object specified by that string binding handle.

Function: CORBA_ORB_ObjectIdList CORBA_ORB_list_initial_services ( CORBA_Object the_orb, {CORBA_Environment *} env)

Locking: Main Invariant holds

Returns a list of service names which can be usefully used in calls to CORBA_ORB_resolve_initial_references(). The type CORBA_ORB_ObjectIdList is a normal CORBA sequence of strings.

Type Manipulation

Function: OPTIONAL(ilu_Class) ILU_C_FindILUClassByTypeName ( RETAIN(ilu_string) type-name )

Locking: L1_sup < otmu, L2, Main unconstrained.

Given the type-name of an ILU object type, of the form "Interface.Typename", returns the ilu_Class value for it. This value can be used to compare types for equality.

Function: OPTIONAL(ilu_Class) ILU_C_FindILUClassByTypeID ( RETAIN(ilu_string) type-id)

Locking: L1_sup < otmu; L2, Main unconstrained.

Given the type-id of an ILU object type, of the form "ilu:gfbSCM7tsK9vVYjKfLole1HOBDc", returns the ilu_Class value for it. This value can be used to compare types for equality.

Function: GLOBAL(OPTIONAL(ilu_string)) ILU_C_ClassName ( RETAIN(CORBA_Object) )

Locking: unconstrained.

Returns the ILU name for the most specific type of an object instance.

Function: GLOBAL(OPTIONAL(ilu_string)) ILU_C_ClassID ( RETAIN(CORBA_Object) )

Locking: unconstrained.

Returns the ILU type ID for the most specific type of an object instance.

Function: ilu_Class ILU_C_ClassRecordOfInstance (CORBA_Object)

Locking: unconstrained.

Returns the ilu_Class value for the most specific type of an object instance.

Function: ILU_C_Class ILU_C_RegisterCustomSurrogateType (ilu_Class kernel-type, ILU_C_Class C-type)

Locking: unconstrained.

This function registers C-type as the kind of class to create an instance of when unmarshalling a surrogate instance of most specific type kernel-type. This should be used in conjunction with the automatically generated function ObjectType__MakeClass() (to create an instance of ILU_C_Class), and the automatically generated constant ObjectType__MSType (the appropriate value for kernel-type). This functionality can be used to implement application-specific surrogate types with caching and other extensions to the basic ILUmodel. The value returned is the previously registered surrogate class for this type.

Function: void ILU_C_RegisterCustomRecord (CORBA_TypeCode record_type, OPTIONAL(ILU_C_CRCreateFn) cfn, OPTIONAL(ILU_C_CRFreeFn) ffn, OPTIONAL(ILU_C_CRPreOutputFn) preoutfn, OPTIONAL(ILU_C_CRPostOutputFn) postoutfn, OPTIONAL(ILU_C_CRPostInputFn) postinfn, CORBA_Environment * env)

Locking: L1 < otmu

Registers a set of functions to be called for a particular record type record_type. The function cfn should create, initialize, and return a value of the desired type; it is called when the kernel is about to unmarshall a value of the type and needs storage for it. The function freefn should perform any clean-up operations necessary for the desired value; they are called before the standard free functions are called on the standard slots of the record type. The function preoutfn should prepare the value to be marshalled to another address space. It may be called repeatedly before the value is actually marshalled, due to sizing requirements imposed by various RPC protocols. The function postoutfn should perform any necessary cleanup after a value has been marshalled; it is expected that this will typically be a NIL function. The function postinfn should perform any initial setup needed after the standard slots of the value have been unmarshalled. This function may raise BAD_PARAM (if the type is not a record type), NO_MEMORY, and INTERNAL.

Object Manipulation

Function: ILU_C_Object * ILU_C_CreateTrueObject ( ILU_C_Class c, OPTIONAL(ilu_string) instance_handle, OPTIONAL(ILU_C_Server) server, void * instanceData, ilu_boolean inside_server )

Locking: inside_server ? Inside(server, most specific type implemented by c) : Main Invariant.

Instantiates a true object of class c, in server server. If the instance_handle is NIL, one will be invented. If the server is NIL, the default server will be used. The instanceData is for the private use of the methods of the class. inside_server is FALSE for normal cases, but TRUE for use in the implementation of an object table.

Function: (OPTIONAL(ILU_C_Object *)) ILU_C_FindObject ( ilu_string server-id, ilu_string instance-handle )

Locking: Main invariant holds.

Given the server-id and instance-handle of an object, returns the object if it exists in the current address space, or ILU_NIL if it doesn't exist. Unlike ILU_C_SBHToObject() and ILU_C_LookupObject(), this function will not create a surrogate for an instance if does not exist -- but if the server-id indicates a server with an object table, the server may create the object dynamically.

Function: ilu_boolean ILU_C_IDOfObject ( CORBA_Object instance, PASS(char **) server-id, PASS(char **) instance-handle )

Locking: Main invariant holds.

Given an instance, returns the server ID and instance handle of that instance. The strings returned are copies; the user must free them with ilu_free when finished with them.

Function: ilu_string ILU_C_SBHOfObject ( CORBA_Object instance )

Locking: Main invariant holds.

Given an instance, returns a reference to that instance. The CORBA-specified routine CORBA_ORB_object_to_string() should typically be used instead.

Function: OPTIONAL(CORBA_Object) ILU_C_SBHToObject (char * sbh, ilu_Class static_type, RETAIN(CORBA_Environment *) Env)

Locking: Main invariant holds.

Takes an object reference and returns the object. static_type is a type the caller knows the object to have.

Function: OPTIONAL(PASS(char *)) ILU_C_FormSBH (RETAIN(char *) server-id, RETAIN(char *) instance-handle, ilu_Class most-specific-type, RETAIN(ilu_ProtocolInfo) pinfo, RETAIN(ilu_TransportInfo) tinfo, RETAIN(CORBA_Environment *) Env)

Locking: Main invariant holds.

Takes necessary information about an object reference, and returns a well-formed ILU string binding handle for that information. This SBH can then be used to create a surrogate instance, using ILU_C_SBHToObject.

Function: OPTIONAL(PASS(char*)) ILU_C_PublishObject ( CORBA_Object instance )

Locking: Main invariant holds.

Publishes the OID of the instance in a domain-wide registry. This is an experimental interface, and may change in the future.

Function: ilu_boolean ILU_C_WithdrawObject ( CORBA_Object instance, PASS(char *) proof)

Locking: Main invariant holds.

Removes the OID of the instance from the domain-wide registry. proof is the string returned from the call to ILU_C_PublishObject().

Function: OPTIONAL(GLOBAL(CORBA_Object)) ILU_C_LookupObject ( RETAIN(char *) sid, RETAIN(char *) ih, ilu_Class static-class )

Locking: Main invariant holds.

Using the local registry, find and return the object specified by the given Server ID and server-relative Instance Handle. static_type is one you know the actual object must have; it may also have more refined types. For an already-reified surrogate this procedure will reconsider what contact info to use for reaching the server.

Function: OPTIONAL(GLOBAL(CORBA_Object)) ILU_C_CreateSurrogateObject ( ilu_Class type, RETAIN(char *) ih, ilu_Server server, ILU_C_ENVIRONMENT *env )

Locking: Main invariant holds.

Create a new object instance of the specified type on the specified server, with the specified ih. If unable to create such an object, return ILU_NIL, and signal the error in env.

This procedure can be used to create new client-side objects for which no true object yet exists. This is the way a client using a server with an object table causes the server to create new instances `on the fly'. When used in this way, the ih must contain all information necessary to allow the server to create the proper true object, as it is the only information passed to the object table's object creation procedure.

Function: CORBA_Object CORBA_Object_duplicate ( CORBA_Object instance, CORBA_Environment * env)

Locking: Main invariant holds.

Increments the reference count of the instance, and returns the instance.

Function: void CORBA_Object_release ( CORBA_Object instance, CORBA_Environment * env)

Locking: Main invariant holds.

Decrements the reference count of the instance. The instance may be destroyed as a result of this operation.

Function: CORBA_unsigned_long CORBA_Object_hash ( CORBA_Object instance, CORBA_unsigned_long max_value, CORBA_Environment * env)

Locking: Main invariant holds.

Returns a hash value for the instance, less than or equal to max_value. Mandated by the CORBA spec.

Function: CORBA_boolean CORBA_Object_is_equivalent ( CORBA_Object instance1, CORBA_Object instance2, CORBA_Environment * env)

Locking: Main invariant holds.

Returns ilu_TRUE if ILU believes the two instances to be the same object, ilu_FALSE otherwise. Compares the servers and kernel objects of the two instances.

Function: void ILU_C_PingObject ( ILU_C_Object instance, ILU_C_ENVIRONMENT * env)

Locking: Main invariant holds.

Attempts round-trip effectless call on object. May raise system exception to indicate failure.

Function: CORBA_boolean CORBA_Object_non_existent ( CORBA_Object instance, CORBA_Environment * env)

Locking: Main invariant holds.

Returns ilu_TRUE if object cannot be successfully pinged. May cause instance to be destroyed.

Function: CORBA_boolean CORBA_Object_is_a ( CORBA_Object instance, CORBA_string repository_id, CORBA_Environment * env )

Locking: Main invariant holds.

Returns ilu_TRUE if the instance supports the interface identified by repository_id. May involve a network round trip.

Function: CORBA_boolean CORBA_Object_is_nil ( CORBA_Object instance, CORBA_Environment * env )

Locking: Main invariant holds.

Returns ilu_TRUE if the instance is the NIL object reference.

Function: CORBA_string CORBA_ORB_object_to_string ( CORBA_Object the_orb, CORBA_Object instance, CORBA_Environment * env )

Locking: Main invariant holds.

Returns a string binding handle for the instance. The argument the_orb is the result of a call to CORBA_ORB_init().

Function: CORBA_Object CORBA_ORB_string_to_object ( CORBA_Object the_orb, CORBA_String string_binding_handle, CORBA_Environment * env )

Locking: Main invariant holds.

Returns a CORBA_Object instance for the specified string_binding_handle. The argument the_orb is the result of a call to CORBA_ORB_init().

Function: OPTIONAL(CORBA_Object) CORBA_ORB_resolve_initial_references ( CORBA_Object the_orb, CORBA_String service_name, CORBA_Environment * env )

Locking: Main invariant holds.

Returns an instance for the service named by service_name, if the system knows of one. The argument the_orb is the result of a call to CORBA_ORB_init(). Raises ex_CORBA_InvalidName if the service_name doesn't name a known service.

Function: ilu_integer ILU_C_SetObjectGCTimeout ( {ILU_C_Object *} the_obj, ilu_integer timeout, {ILU_C_ENVIRONMENT *} env)

Locking: Main invariant holds.

Sets the GC timeout of the_obj to timeout. Returns the previous timeout when successful. The GC timeout of an instance is the amount of time ILU will wait before collecting an object without references. This timeout accommodates references to the object that are in transit on the network, and its value should be related to typical maximum network delays on the network being used. Setting the timeout to a value less than the network delay may result in objects being prematurely collected.

Identity Functions

Function: ilu_Passport ILU_C_CreatePassport (OPTIONAL(PASS(ilu_IdentityInfo)) info, ILU_ERRS((no_memory)) *err)

Create and return a new passport object. If an identity info is passed in, will put that identity in the new passport.

Function: ilu_IdentityInfo ILU_C_CopyIdentity (RETAIN(ilu_IdentityInfo) info, ILU_ERRS((no_memory)) *err)

Creates and returns a copy of the identity in info.

Function: ilu_boolean ILU_C_AddIdentity (RETAIN(ilu_Passport) pp, PASS(ilu_IdentityInfo) info, ILU_ERRS((no_memory)) *err)

Adds the specified identity to the specified passport, which now owns the identity storage.

Function: OPTIONAL(RETAIN(ilu_IdentityInfo)) ILU_C_FindIdentity (RETAIN(ilu_Passport) pp, RETAIN(ilu_IdentityType) ident_type)

If the passport pp contains an identity of the specified type, returns a pointer to it, otherwise returns ILU_NIL. The passport retains ownership of the identities storage; the caller may make a copy of the identity by calling ILU_C_CopyIdentity.

Several identity types are pre-defined. The identity type ilu_ConnectionIdentity is always defined; it consists of a string which describes the connection used by the caller to communicate with the server in a colloquial fashion. The identity type ilu_SunRPCAuthUnixIdentity is defined if the Sun RPC protocol has been configured in; it provides a struct containing the various pieces of information specified by the protocol specification. The identity type ilu_GSSIdentity is available if support for the secure transport has been configured in; it supports a variety of identity schemes under the GSS umbrella. See section Security and the file `ILUSRC/runtime/kernel/iluxport.h' for more information on identities and identity schemes.

Function: ilu_cardinal ILU_C_DisplayIdentity ( RETAIN(ilu_IdentityInfo) identity, RETAIN(char *) buf, ilu_cardinal bufsize, {ILU_C_ENVIRONMENT *} env)

Formats a textual display of the identity into buf, respecting bufsize. Returns the actual length of the string (the amount of the buffer that was actually used). May raise an exception through env.

Function: ilu_boolean ILU_C_DecodeGSSIdentity ( RETAIN(ilu_IdentityInfo) identity, OPTIONAL(gss_name_t *) name, OPTIONAL(ilu_FineTime *) good_till, OPTIONAL(gss_OID) mech, OPTIONAL(ilu_boolean) localp, OPTIONAL(OM_uint32 *) flags, {ILU_C_ENVIRONMENT *} env)

Only available if ILU has been configured with support for the GSS security transport. Returns various aspects of the GSS identity in the output parameters name, good_till, mech, localp, and flags. If no return value is specified for an output parameter, that output parameter is not returned. May raise an exception through env, in which case the return value is ilu_FALSE. If no exception is signalled, the return value is ilu_TRUE.

The meaning of the output parameters are as follows:

See `ILUSRC/GSS/kernel/gssapi.h' for a definition of the GSS types gss_name_t, gss_OID, and OM_uint32.

Function: OPTIONAL(PASS(ilu_string)) ILU_C_GSSNameToString ( RETAIN(gss_name_t) gss_name, {ILU_C_ENVIRONMENT *} env)

Returns a newly-malloced string containing a textual representation of the principal name in gss_name. May raise an exception through env, in which case returns ILU_NIL.

See `ILUSRC/GSS/kernel/gssapi.h' for a definition of the GSS type gss_name_t.

Function: ilu_IdentityInfo ILU_C_AcquireGSSIdentity (gss_cred_id_t c, {ILU_C_ENVIRONMENT *} err)

Available only if the security transport filter has been configured in. Returns an ilu_IdentityInfo corresponding to the given GSS credentials. May raise an exception through err, in which case returns ILU_NIL.

See `ILUSRC/GSS/kernel/gssapi.h' for a definition of the GSS type gss_cred_id_t.

Function: OPTIONAL(gss_cred_id_t) ILU_C_AcquireGSSCredForName (char *name, ilu_cardinal lifetime, gss_OID secmech, ilu_boolean accept_only, {ILU_C_ENVIRONMENT *} err)

Available only if the security transport filter has been configured in. Returns GSS credentials given the cannonical inputs, which are

May raise an exception through err, in which case a value of ILU_NIL is returned.

See `ILUSRC/GSS/kernel/gssapi.h' for a definition of the GSS types gss_OID and gss_cred_id_t.

Function: ilu_IdentityInfo ILU_C_AcquireSunRPCAuthUnixIdentity (ilu_string hostname, ilu_shortcardinal uid, ilu_shortcardinal gid, ilu_shortcardinal ngroups, ilu_shortcardinal* groups, ILU_C_ENVIRONMENT * env)

Available only if SunRPC UNIX Authorization has been configured in. Returns an ilu_IdentityInfo corresponding to the given UNIX credentials.

Function: ilu_boolean ILU_C_SetPassportContext (ilu_Passport pp)

Sets the special hidden per-thread slot for passports to contain pp. The slot retains that value until explicitly changed later.

Function: ilu_Passport ILU_C_GetPassportContext (void)

Returns the value in the special hidden per-thread slot for ilu_Passports.

Function: ilu_boolean ILU_C_DestroyPassport (PASS(ilu_Passport) pp, ilu_Error * err)

Deallocates the storage associated with the passport, and any associated identities.

Function: OPTIONAL(ilu_Passport) ILU_C_CallerIdentity (void)

Returns the passport associated with the caller, or possibly ILU_NIL if being invoked directly in a thread with no passport set. This procedure should only be invoked inside the scope of a true method.

Call Management Functions in C

Function: ILU_C_Serializer ILU_C_CreateSerializationContext (ILU_C_Server S, ILU_C_ENVIRONMENT *env)

Creates a new instance of the serialization guarantee; this instance is applicable only to calls on objects of S.

Function: ilu_boolean ILU_C_ReleaseSerializer (ILU_C_Serializer si, ILU_C_ENVIRONMENT *env)

A client calls this after it is done using the given ilu_Serializer.

Function: ilu_boolean ILU_C_SetSerializationContext (ILU_C_Serializer x)

Sets the special hidden per-thread slot for ILU_C_Serializers to contain x. The slot retains that value until explicitly changed later.

Function: ILU_C_Serializer ILU_C_GetSerializationContext (void)

Returns the value in the special hidden per-thread slot for ILU_C_Serializers.

Function: ILU_C_Batcher ILU_C_CreateBatcher (ilu_FineTime timeout, ilu_boolean pushable, ILU_C_ENVIRONMENT *env)

Creates a new batcher.

Function: ilu_boolean ILU_C_ReleaseBatcher (ILU_C_Batcher val, ILU_C_ENVIRONMENT *env)

A client calls this after it is done using the given ILU_C_Batcher.

Function: ilu_boolean ILU_C_SetBatcherContext (ILU_C_Batcher x)

Sets the special hidden per-thread slot for ILU_C_Batchers to contain x. The slot retains that value until explicitly changed later.

Function: ILU_C_Batcher ILU_C_GetBatcherContext (void)

Returns the value in the special hidden per-thread slot for ILU_C_Batchers.

Function: ilu_boolean ILU_C_PushBatcher (ILU_C_Batcher b, ILU_C_ENVIRONMENT * env)

Initiates delivery of all buffered call messages associated with b.

Function: ILU_C_Pipeline ILU_C_CreatePipeline (ILU_C_ENVIRONMENT *env)

Creates a new pipeline.

Function: ilu_boolean ILU_C_ReleasePipeline (ILU_C_Pipeline pl, ILU_C_ENVIRONMENT *env)

A client calls this after it is done using the given ILU_C_Pipeline.

Function: ilu_boolean ILU_C_SetPipelineContext (ILU_C_Pipeline x)

Sets the special hidden per-thread slot for ILU_C_Pipelines to contain x. The slot retains that value until explicitly changed later.

Function: ILU_C_Pipeline ILU_C_GetPipelineContext (void)

Returns the value in the special hidden per-thread slot for ILU_C_Pipelines.

Exception Information in C

Function: GLOBAL(const char *) ILU_C_SysExnMinorDescr ( CORBA_Environment * Env )

Locking: Main Invariant holds.

If Env indicates a system exception has been raised, and the system exception's minor code is ILU-specific, returns a string that describes the minor code. Otherwise returns nil.

Function: GLOBAL(const char *) ILU_C_Exception_SrcFile ( CORBA_Environment * Env )

Locking: Main Invariant holds.

If Env indicates a system exception has been raised, and it was raised locally in the ILU runtime support, returns the name of the source file in which the raise statically occurs. Otherwise returns nil.

Function: int ILU_C_Exception_SrcLine ( CORBA_Environment * Env )

Locking: Main Invariant holds.

If Env indicates a system exception has been raised, and it was raised locally in the ILU runtime support, returns the line number where the raise statically occurs. Otherwise returns 0.

Server Manipulation

Macro Function: ilu_boolean ILU_C_USE_OS_THREADS

Locking: Main invariant holds.

This macro expands to a function call. If ILU has been configured with os-level thread support, calling this routine will `turn on' that thread support for use with C. This means that a new thread will be forked to handle each incoming connection, in servers, and if the wire protocol being used permits it, a thread will be forked to handle each incoming request. This routine returns FALSE, and emits an error message, if something goes wrong with enabling thread support. It must be called before making any other ILU calls, and before initializing any interfaces via calls to interface__Initialize or interface__InitializeServer.

Macro Function: void ILU_C_FINISH_MAIN_THREAD ( int returnvalue )

Locking: Main invariant holds.

This routine will return from the `main' thread with the specified value. In some thread systems, the program will be terminated when the main thread returns from main(), regardless of whether other threads are running. For these thread systems, this call will simply cause the main thread to idle forever, instead of returning.

Function: void ILU_C_Run (void)

Locking: Main invariant holds.

Called to animate a server and/or other parts of the program. Used only in single-threaded mode. Invokes the event handling loop. Never returns.

Function: OPTIONAL(ILU_C_Server) ILU_C_FullInitializeServer (OPTIONAL(RETAIN(char *)) serverID, OPTIONAL(GLOBAL(ILU_C_ObjectTable)) obj_tab, OPTIONAL(RETAIN(ilu_ProtocolInfo)) protocol, OPTIONAL(RETAIN(ilu_TransportInfo)) transport, OPTIONAL(RETAIN(ilu_Passport)) identity, ilu_boolean createPortAnyway, ilu_boolean port_public)

Locking: Main invariant holds.

Creates and returns an ilu_Server with ID serverID, object mapping table obj_tab, using protocol protocol over a transport stack specified by transport. If serverID is specified as NULL, a unique string is generated automatically for the server ID. If obj_tab is specified as NULL, the default hash table object table is used.

If either protocol or transport is specified, or if createPortAnyway, an ilu_Port will automatically be created and added to the ilu_Server. protocol, if not NULL, is a string that specifies which RPC protocol to use on the port; NULL causes use of the default protocol. transport, if not NULL, is a sequence of strings that specifies the transport stack to use below the RPC protocol; NULL signifies use of the default transport to/from one of the IP addresses of this host. See section Protocols and Transports for details on protocols and transports. If an identity is specified, it may be used for communications security purposes. If an ilu_Port is called for, it will become the default port of the ilu_Server, and will be public iff requested.

Function: ilu_boolean ILU_C_FullAddPort (ILU_C_Server server, OPTIONAL(RETAIN(ilu_ProtocolInfo)) protocol, OPTIONAL(RETAIN(ilu_TransportInfo)) transport, OPTIONAL(RETAIN(ilu_Passport)) identity, ilu_boolean makeDefault, ilu_boolean port_public, ILU_C_ENVIRONMENT * env)

Locking: Main invariant holds.

Creates a new ilu_Port for the server. protocol, transport, identity, and port_public parameterize the ilu_Port as for ILU_C_InitializeServer.

Function: ilu_boolean ILU_C_AddCInfo (ILU_C_Server server, OPTIONAL(RETAIN(ilu_ProtocolInfo)) protocol, OPTIONAL(RETAIN(ilu_TransportInfo)) transport, ILU_C_ENVIRONMENT * env)

Locking: Main invariant holds.

Adds the given contact info to the given kernel server; used for contact info for ports on other kernel servers of the same server.

Function: ilu_boolean ILU_C_Server_CInfo (ILU_C_Server server, ilu_boolean want_public, char ** protocol, ilu_TransportInfo * transport, ILU_C_ENVIRONMENT * env)

Locking: Main invariant holds.

Obtains the first (if any) public or private (as requsted) contact info sequence of the given server. Caller owns storage pointed to by protocol and transport. On success: callee returns TRUE; callee allocates new storage for string and ilu_TransportInfo and returns ownership to caller by storing pointers through protocol and transport. On failure: callee returns FALSE.

Function: (RETAIN(ilu_string)) ILU_C_IDOfServer ( ILU_C_Server server )

Locking: Main invariant holds.

Returns a pointer to the server id of the specified server.

Function: ILU_C_ObjectTable ILU_C_CreateObjectTable (CORBA_Object (*object_of_ih)(ilu_string instance-handle, ilu_private user-data), void (*free_user_data)(ilu_private user-data), ilu_private user-data )

Locking: Main invariant holds.
Locking for object_of_ih: L1 >= {server}, L1 >= {gcmu} if result is true and collectible; L2, Main unconstrained.
Locking for free_user_data: L1 >= {server}; L2, Main unconstrained.

Creates and returns a value of type ILU_C_ObjectTable encapsulating the two procedures object_of_ih and free_user_data, and the user-specified data element user-data. When object_of_ih is called, it should create an appropriate CORBA_Object with the specified instance handle, and return it. When free_user_data is called, it indicates the end of the object table, and free_user_data should free up any storage associated with user-data.

An object table is associated with a kernel server by passing the object table as a parameter to the function ILU_C_InitializeServer. A single object table may be used with multiple different ilu_Server instances.

C Procedure Type: ILU_C_ServerRelocateProc

Locking: L1 >= {ilu_Server}; L2 unconstrained

typedef ilu_boolean
  (*ILU_C_ServerRelocateProc) (ILU_C_Server server,
			       ilu_private argument,
			       OPTIONAL(ilu_ProtocolInfo *) new_pinfo,
			       OPTIONAL(ilu_TransportInfo *) new_tinfo);
This function should return TRUE if new pinfo and tinfo have been stored into the out parameters; otherwise it should return FALSE. It has no mechanism for signalling errors. It is called by a protocol implementation to see if the server wants the caller to be redirected to another location or cinfo stack.

Function: OPTIONAL(void *) ILU_C_SetServerRelocationProc (ILU_C_Server server, ILU_C_ServerRelocateProc relocation_fn, {void *} relocation_arg, ILU_C_ENVIRONMENT * metavar{env})

Locking: Main invariant holds

Ensures that the function relocation_fn will be called with relocation_arg as an argument whenever a request comes in for an object maintained by server, on any connection which uses a relocating protocol (currently only w3ng is a relocating protocol). See the definition of ILU_C_ServerRelocateProc for a description of how it is used. The returned value is the previous value of relocation_arg, if any.

Function: ilu_cardinal ilu_tcp_SetDefaultBuffersize (ilu_cardinal new-buffer-size)

Locking: Main invariant holds.

Sets the default buffersize, in bytes, for TCP transports to new-buffer-size. This default can be overridded by explicitly specifying a buffersize in the tinfo for the port or object. Returns the previous default value. This function is only available if ILU has been configured with support for the TCP/IP transport.

Function: void ilu_tcp_GetStats ( ilu_cardinal * bytes-sent, ilu_cardinal * bytes-read, ilu_cardinal * moorings-created, ilu_cardinal * connections-accepted, ilu_cardinal * connections-opened, ilu_cardinal * currently-open-connections, ilu_cardinal * max-simultaneously-open-connections )

Locking: Main invariant holds.

Returns various statistics about the TCP/IP tranports use of various resources for this process. The values returned are the values since the process was started, or since the reset function ilu_tcp_InitializeStats() was last called, except for currently-open-connections, which is unaffected by the reset function. This function is only available if ILU has been configured with support for the TCP/IP transport.

Function: void ilu_tcp_InitializeStats ()

Locking: Main invariant holds.

Resets the statistics counters for this process. This function is only available if ILU has been configured with support for the TCP/IP transport.

Function: ilu_FineTime ILU_C_SetDefaultGCPingPeriod ( ilu_FineTime new_period, {ILU_C_ENVIRONMENT *} env)

Locking: L1 < gcmu

The ILU distributed garbage collection protocol detects defunct clients by periodically pinging their GC callback objects. If a client's callback object cannot be successfully pinged, it is removed from the list of clients which have references to any objects on the server. This call sets the ping period to new_period. Only GC callback objects registered after this call will use the new period. Returns the previous ping period upon success.

CORBA Compatibility Macros

ILU supports CORBA 2.0, and formerly supported either 1.1 or 1.2, depending on how it was installed at your site. A number of macros are defined to make programs less dependent on which version they use.

Macro: ILU_C_OBJECT

Expands to CORBA_Object.

Macro: ILU_C_ENVIRONMENT

Expands to CORBA_Environment.

Macro: ILU_C_NO_EXCEPTION

Expands to CORBA_NO_EXCEPTION.

Macro: ILU_C_USER_EXCEPTION

Expands to CORBA_USER_EXCEPTION.

Macro: ILU_C_SYSTEM_EXCEPTION

Expands to CORBA_SYSTEM_EXCEPTION.

Macro: ILU_C_SUCCESSFUL ( ILU_C_ENVIRONMENT * ev )

Evaluates to true if no exception has been raised.

Macro: ILU_C_SET_SUCCESSFUL ( ILU_C_ENVIRONMENT * ev )

Sets ev to a successful result.

Macro: ILU_C_EXCEPTION_ID ( ILU_C_ENVIRONMENT * ev )

Returns the char * value that is the exception's ID.

Macro: ILU_C_EXCEPTION_VALUE ( ILU_C_ENVIRONMENT * ev )

Expands to CORBA_exception_value(ev).

Macro: ILU_C_EXCEPTION_FREE ( ILU_C_ENVIRONMENT * ev )

Expands to CORBA_exception_free(ev).

Go to the previous, next section.