COBS Object Transport Layer API

The API for the COBS Object Transport Layer is pretty simple... How it all really works is another matter. The basic API follows:
typedef struct _object_ref {
    /* attributes which name object globally */
  attr_list obj_name;
    /* caches meaning of name in local context */
  attr_list name_trans_cache;
    /* method specific attribute cache */
  attr_list method_specific_cache;
} object_ref_struct, *object_ref;

/* opaque type for completion handle */
typedef struct _handle_struct *otl_handle;

/* 
 * perform an invocation.
 * (sync or async, simple or return)
 */
extern otl_handle 
otl_invoke(object_ref obj, attr_list method_attrs, 
           void *pblock, int plen );

otl_is_local_object( object_ref obj );
extern void otl_wait( otl_handle handle );

extern int otl_query_done( otl_handle handle );

extern int otl_release_handle( otl_handle handle );

A sample piece of application code to perform an invocation might look like the text below. (Here, by "application code", I mean a chunk of code that calls OTL. This code may be generated by an IDL processor generator or other object-system-specific code generator.)

Incoming values:

attr_list obj;
an object pointer (represented as an attribute list)
method_name
which may or not be static
other
presumably some parameters, etc.
{
  /*
   * the otl_is_local_object() call returns quickly if the
   * object reference has been seen before and thus name
   * translation results are cached in the object reference
   * (in obj->name_trans_cache ) 
   */
  if (otl_is_local_object(obj)) {
    /* application specific code to perform local invocation */
  } else {
    /*
     * application specific code to marshall parameters. 
     * result is values pblock and plen.
     */
    attr_list method_attrs;
     /*
      * At this point, method-specific attributes are 
      * cached somehow in the obj attribute list.  Construct 
      * a query, get them out and put them in method_attrs.
      */
     otl_handle handle = otl_invoke(obj, method_attrs, pblock, plen);
     otl_wait(handle);
     /*
      * Results are available now and attribute caches are
      * updated. 
      */
     otl_release_handle(handle);
  }
}
This code makes use of most of the basic OTL calls. In particular, it uses:
otl_is_local_object() to determine object locality.
How this is determined is explained in the skeleton code of the routine given below. Use of this routine is not necessary since otl_invoke() will perform local invocations as well, but it may be valuable to avoid parameter packing overhead.
otl_invoke() to perform the invocation.
otl_invoke() will perform any invocation, either local or remote. Its precise behavior is determined by the attributes associated with the object. Its parameter handling is limited to transmitting an uninterpreted block of memory and making it available to the called routine.
otl_wait() to wait for the invocation to complete.
The handle returned by the otl_invoke() function also holds pointers to the original object reference so that the attribute caches there can be updated if necessary.
otl_release_handle() frees the resources held by the handle.
This may be called immediately after otl_invoke() if the return value is unnecessary and asynchronous operation is desired.
[ Some additional functions will be added to allow return value information to be extracted from the handle. ]
/*
 * otl_is_local_object()
 * This otl utility routine exists to determine object
 * locality.  This determination may require several steps,
 * including performing name translation if that is
 * required.  Assuming that no name translation is required,
 * either the obj->object_name or the obj->name_trans_cache
 * will contain a OTL:HOME_HOST attribute which will be
 * compared to the current host.  If they are equivalent,
 * the attribute OTL:IS_LOCAL will be added to
 * obj->name_trans_cache.  (This attribute is checked for at
 * the beginning of the routine as a shortcut to this
 * process.)  If the OTL:HOME_HOST is not present, the
 * OTL:NAME_SERVER attribute must hold a value that can be
 * invoked to translate the name and set the OTL:HOME_HOST
 * attribute. 
 */
otl_is_local_object(obj)
object_ref obj;
{
  static atom_t is_local_atom = 0;
  static atom_t home_host_atom = 0;
  static atom_t my_host_atom = 0;
  
  atom_t home_host;
  int local_call = 0;

  if (my_host_atom == 0) {
    my_host_atom = otl_get_local_host_atom();
  }
  if (is_local_atom == 0) {
    is_local_atom = atom_from_str("OTL:IS_LOCAL");
  }
  if (obj->name_trans_cache && 
      query_attr(obj->name_trans_cache, is_local_atom, 
                 /* type pointer */ NULL,
                 /* value pointer */ NULL)) {
    return 1;
  } else {
    if (home_host_atom == 0) {
      home_host_atom = atom_from_str("OTL:HOME_HOST");
    }
    if (!query_attr(obj->obj_name, home_host_atom, 
                    /* type pointer */ NULL,
		    /* value pointer */ &home_host) &&
        !query_attr(obj->name_trans_cache, home_host_atom, 
                    /* type pointer */ NULL,
		    /* value pointer */ &home_host)) {
      /* 
       * OTL invoke to the value of the OTL:NAME_SERVER
       * attribute, followed by an otl_wait() and
       * otl_release_handle().  Upon completion, there
       * should be a home_host value in
       * obj->name_trans_cache. 
       */
      if (!query_attr(obj->name_trans_cache, home_host_atom, 
                       /* type pointer */ NULL, 
		       /* value pointer */ &home_host)) {
        return 0; /* Serious error */
      }
    }
    return (home_host_atom == my_host_atom);
  }
  /*NOTREACHED*/
}



extern
atom_value
otl_get_local_host_atom()
{
    char hostname[256];
    char *domainname;
    gethostname(hostname, 256);
    strcat(hostname, ".");
    domainname = rindex(hostname, ".") +1;
    getdomainname(domainname, 192);
    return atom_from_str(hostname);
}