ADA 83/95 BINDINGS TO OSF’S DISTRIBUTED COMPUTING ENVIRONMENT (DCE)

Richard Kram

Jeffrey Den Bleyker

Howard Eng

(Unixpros Inc.)

Ed Gallagher

(US ARMY - CECOM, Fort Monmouth)

1.0 INTRODUCTION

If Ada 95 is to succeed in the commercial marketplace, it must include facilities for programming large-scale, heterogeneous, distributed, and possibly enterprise-wide, client/server-based systems. The new Ada standard (ANSI/ISO/IEC-8652:1995) includes a Distributed Annex (E) which “defines facilities for supporting the implementation of distributed systems using multiple partitions working cooperatively as part of a single Ada program” 1. The Ada 95 Rationale further clarifies the basic objective when it states that “the language shall facilitate the distribution and dynamic reconfiguration of Ada applications across a homogeneous distributed architecture” 2. These definitions seem somewhat limited when applied to large heterogeneous client/server systems comprised of many inter-operative programs. Clearly, third-party distributed programming interfaces will be required if Ada is to be used to program large-scale distributed systems or to interface with current client/server applications.

Although the Distributed Annex attempts to standardize a distributed computing environment for Ada programs, it is more a set of guidelines based on the concepts of Active and Passive Partitions and Remote Subprogram Calls than a detailed specification for the creation of a full-featured distributed programming interface such as can be found in the Open Software Foundation’s (OSF) Distributed Computing Environment (DCE) or the Common Object Request Broker Architecture (CORBA).

The Distributed Annex does not attempt to specifically

address numerous requirements of the exploding client /

server market. Fortunately, it is flexible enough to be made compatible with almost any multiprocessing or distributed system that supports shared memory and/or the Remote Procedure Call (RPC). In fact, any Ada environment that simply supports a Remote Call Interface Library Unit might be considered compatible with the Distributed Annex. The authors of the Distributed Annex must have had this in mind when formulating Ada’s distributed programming semantics. If too many rules and restrictions were imposed, simple interfaces to established distributed computing environments might not have been possible.

It is unlikely that Ada compiler vendors will have the resources to create full-featured distributed programming environments. The more cost effective and productive solution is to provide Ada bindings to DCE and other well-established distributed computing environments. While these bindings may not be totally compatible with the semantics of the Distributed Annex, an intermediate layer of wrappers could be provided that satisfies the few requirements imposed by the Annex (although it is unlikely that commercial firms committed to Ada will feel bound by these loosely defined Annex E rules).

This paper describes general rehosting issues and the specific methodology we used to create a set of Ada bindings to OSF’s DCE, particularly the RPC service Application Programming Interface (API). It is based on work being performed by Unixpros for PEO CCS, US Army CECOM, as well as for a Small Business Innovative Research (SBIR) project for the US Army CECOM.These bindings are now being used in production-level code for large-scale Army communications systems. They were initially designed to operate with Alsys Ada and have since been rehosted to run with the GNU Ada Translator (GNAT) Ada 95 translator. In addition, they are currently being rehosted for Rational’s Verdix Ada Development System (VADS).

1.1 DCE USAGE

OSF’s DCE is one of a few distributed computing environments that have become de facto industry standards. No doubt, this is largely due to OSF support by IBM, DEC, HP and Siemens, which include DCE in their product offerings, in some cases bundling it into their operating systems. Third party DCE support for SUN and a variety of non-OSF systems from PCs to supercomputers is also available.

We have found the DCE to be a full-featured distributed computing environment which has allowed us to easily and effectively integrate large-scale communications subsystems which are part of the Joint Services Common Operating Environment (COE). In addition to military requirements, numerous commercial firms (especially financial concerns) are adopting or are considering the DCE as a method of creating efficient enterprise-wide systems. DCE is thus one of the most desirable candidates for distributed Ada bindings.

1.2 DCE SERVICES

The DCE has integrated facilities for secure remote procedure calls, multilevel security and access control, local and global directory access and management, distributed file access and management, distributed time management, remote system synchronization, diskless support, configuration management and POSIX thread support. Ada applications need not use all of these features, but the DCE runtime will still support them. DCE’s services are logically arranged in a hierarchical order, which can be seen in Figure 1.

Figure 1 - DCE SERVICES

It is not this paper’s intent to give a full description of DCE and associated programming techniques. This can be found in OSF’s DCE documentation set 3-7 and other sources 8-10.

1.3 ADA/DCE INTEGRATION ISSUES

The DCE is C-based and includes two major elements that must be addressed before an Ada binding interface can be implemented: 1) DCE runs over POSIX threads and 2) DCE relies on an Interface Definition Language (IDL) to compile client/server stubs prior to running distributed applications. Once these issues have been resolved, the actual Ada bindings and associated wrappers and helper functions can be written.

1.3.1 DCE’S POSIX THREAD INTERFACE

All of the DCE’s services make runtime calls which eventually call POSIX thread (pthread) routines to create threads of execution, mutexes, condition variables, etc. This can cause a number of Ada binding implementation problems.

First, most Ada compilers do not use POSIX threads to implement tasking. These non-threaded Ada runtimes create and maintain their own Task Control Blocks (TCB) which are used to store and retrieve relevant execution parameters during context switches. The Ada tasking mechanism is also frequently optimized for a particular vendor’s runtime environment.

Major problems can result if a non-threaded Ada runtime makes DCE calls. Depending on the Ada runtime implementation, tasking/thread incompatibilities might lead to a number of catastrophic problems including deadlock, inconsistent execution and even system crashes. (See section 2.3 for more details on DCE bindings issues)

In practice, these incompatibility problems can be solved using one of two rehosting methods, depending on the Ada host environment: 1) the Ada runtime can be modified to use POSIX threads as the underlying tasking mechanism and also for implementing protected objects and other Ada 95 features that interface with the tasking runtime or 2) a POSIX-thread library can be implemented using Ada tasks which will then be called by the DCE runtime. Both of these solutions maintain only one low level context switching procedure, either as standardized and portable POSIX threads or as vendor-specific Ada tasks.

It must be noted that both of these solutions have advantages and limitations. Using POSIX threads provides a standards-based runtime that (at least theoretically) guarantees portability and also provides a method of interfacing external POSIX-based routines/subsystems to both Ada and DCE code without having to understand the low level runtime context switching mechanism. However, POSIX threads’ reliance on mutexes and condition variables (which can require a high runtime maintenance overhead) will most likely achieve lower levels of performance than an Ada runtime optimized for real-time execution using simple pointer reassignments for TCB switching.

Implementing a POSIX library using Ada tasks requires some knowledge of the Ada runtime, but is much less time consuming than rehosting the Ada runtime to use POSIX threads. In this case, the Ada runtime is left in tact and only those POSIX routines used by DCE need be rehosted. This might also seem like an efficient rehost method as it directly accesses the (hopefully) optimized Ada runtime. In practice however, we have found that many Ada tasks have to be created to simulate the POSIX environment used by DCE. This can negate or cancel any speed advantages over a native Ada tasking implementation using DCE/POSIX threads.

In the end, the actual Ada/DCE integration method chosen must heavily weight cost and access issues. Adding a POSIX-based runtime to an existent non-POSIX Ada compiler is both prohibitively time consuming and risky if undertaken by a third party. It is for this reason that we implemented our Alsys Ada 83 to DCE bindings using an Ada task-based POSIX-thread library.

If the Ada runtime already uses POSIX threads, (as in GNAT) then the rehost method is clear. In this case there will normally be two pthread libraries which must be integrated (that used by the Ada compiler and the DCE Thread library). These two libraries will almost certainly contain slight differences which must be resolved, most likely by using wrappers.

Unfortunately, DCE uses Draft 4 of the POSIX thread standard and POSIX does not guarantee compatibility between any two drafts of the standard. Thus, an interface must be written to resolve any discrepancies between the two threaded runtimes. This normally involves different naming conventions for the same routine or in some cases the addition or deletion of a routine. This is exactly the approach we adopted when creating DCE bindings for the GNAT runtime, which maintains Draft 6 pthread compatibility. In practice, we assume that any new pthread-based Ada 95 implementation will use the final draft of the POSIX threads standard, but OSF does not plan to upgrade to the final draft as yet. It still maintains Draft 4 compatibility for the latest DCE 1.1 release.

1.3.2 DCE, IDL AND ADA

The DCE uses client and server stubs which in turn make calls to DCE runtime routines to set up handles and connections, marshall and unmarshall data, set communication modes, etc. Much of this process is automated for the user through the use of the DCE’s C-like Interface Definition Language (IDL) and its associated compiler. The IDL compiler is an integral part of DCE and it can not be eliminated without requiring the user to gain a low level knowledge of the DCE runtime environment, which is a requirement we do not wish to force on the programmer. Three primary IDL rehost options exist:

1) Redesign the IDL to be Ada compatible and then rehost/rewrite the IDL compiler to compile the new IDL.

2) Write a translator which will parse the user’s Ada code and automatically create IDL client/server stubs.

3) Require the user to write their own C/IDL stubs that will interface to the Ada code.

While the first option might be most preferable in the long term, it certainly is too costly to consider for the initial Ada/DCE bindings effort. The second option is much more feasible, but still is costly and prone to errors until all of the Ada/IDL typing compatibility issues are resolved. We started such a translator and have it working for simple Ada types, but it has to be enhanced to handle complex types: arrays, strings, records, dynamic data structures, protected types, etc. The translator parses Ada 83 client and server code and inserts calls to IDL client and server stubs, which it generates from the Ada source. The IDL code is then run through the standard DCE IDL compiler without any required alterations. A command script is then run that links all the functional components together and creates a DCE-based client/server application.

The third IDL option is in effect the only viable short term solution. This involves having the user create IDL stubs using DCE’s IDL I/O argument types and then write interfacing routines that make the C code generated by the IDL compiler compatible with the corresponding Ada subprograms’ data types. This requires the user to 1) learn IDL programming and 2) know how to map Ada arguments to external C function arguments.

1.3.2.1 CREATING ADA/IDL INTERFACES

A short example will be given to better illustrate the manual technique now required to interface Ada code to IDL generated stubs. The top-level block diagram of the typical Ada/DCE client/server file structure can be seen in Figure 2.

Suppose we want an Ada client to call a simple Ada RPC server routine that increments a number. The routine to increment the number is trivial:

procedure INC_INT(N: in out INTEGER) is

begin

N := N + 1

end INC_INT;

However, this call must be executed as a DCE RPC on a distributed network. DCE components must be registered and accessed using binding handles. These handles are used as arguments in the IDL code that is compiled to create the C client and server stubs that establish the actual distributed connections. The Ada code must use these binding handles and system addresses to access the RPC, which is defined in the IDL code. One possible method of creating this interface is to use a helper routine as the real RPC (XX_INC_INT):

procedure INC_INT(N: in out INTEGER) is

begin

N := N + 1

end INC_INT;

procedure XX_INC_INT (BINDING_HANDLE: in

RPC.RPC_BINDING_HANDLE_TYPE;

N : in SYSTEM.ADDRESS) is

XX_N: INTEGER;

begin

XX_N := FETCH_INT_FROM_ADDRESS(N);

INC_INT(XX_N);

ASSIGN_INT_TO_ADDRESS(N,XX_N);

end XX_INC_INT;

The helper function uses a temporary variable to store, compute and assign the actual value passed to the internal increment routine. The IDL interface for the RPC will then be:

interface sample_rpc

{

void xx_inc_int(

[in] handle_t h,

[in, out] long int *N);

}

Figure 2 - ADA/DCE File Structure

Of course, the Ada code could be combined into one RPC routine, but this makes the function of the increment routine more obscure as it will be embedded within the RPC interface code. This trivial example illustrates that the user must think about the RPC interface when writing the Ada code. Some knowledge of DCE coding practice is obviously required. There are also a number of DCE initialization calls that must be made in the client and server to import bindings, set protocols, start the server listening for clients, etc. The reader should consult the DCE programming references at the end of this article for more information on DCE RPC interfacing.

1.3.3 DCE VERSIONS

Finally, a few words should be said about DCE versions. Most current DCE installations are running older versions of DCE (V1.0 and most probably V1.03). The Ada/DCE bindings described in this paper currently function with version 1.03. The latest version of DCE (V1.1) has been in the field for about six months and adds a number of important features including:

  • Improved Administration Functions with a consolidated remote user interface
  • Cell Aliasing and Hierarchical Cells
  • Improved Security with delegation, auditing and numerous registry extensions
  • Internationalized Interfaces and Character Code Set Interoperability
  • Performance Enhancements to IDL to generate smaller, more efficient code and add more flexible typing mechanism
  • RPC enhancements allowing improved throughput, optimized runtime packets and fast transport support for FDDI, etc.
  • DFS/NFS gateway and DFS delegation
  • Remote administration of Distributed Time Service

We are now rehosting the bindings to run with DCE 1.1.

2.0 THE ADA 83 TO DCE BINDINGS

The development of the Ada bindings to the DCE RPC services had two principal goals:

1.Develop bindings that provide clean, reliable and portable access to the DCE API.

2.Develop bindings that do not alter the DCE component’s API from that of the standard C language implementation.

Further requirements of the first goal were that we:

  • Keep the bindings as ‘thin’ as possible
  • Provide full Ada semantics for parameters and results
  • Keep the bindings as simple as possible by using straightforward Ada code
  • Use private types whenever practical to keep the bindings as secure as possible from a programming standpoint.

Further requirements of the second goal were that we:

  • Retain DCE API subprogram names
  • Minimize the creation or removal of subprograms from the DCE API
  • Minimize the addition or deletion of parameters from DCE API calls.

2.1 ADA BINDINGS TO DCE RPC

The Ada bindings to the DCE RPC component are divided into several packages: SYSTEM (provided by the Ada compiler vendor), support packages C_STRING_PKG, DCE_STATUS, DCE_BASE and DCE_BASE_IO and the main RPC component package. A set of helper functions is also included for interfacing C and Ada code. Each DCE routine/subprogram is implemented as a separate compilation unit. A complete description of each of the bindings is available in an internal document 15.