UC++ v1.0: Language Definition and Semantics

by T. O'Brien (ICL), G. Roberts, M.Wei, R. Winder
Last revision: 12 May 1994

Table of Contents

Abstract UC++ v1.0: Language Definition and Semantics

Deptartment of Computer Science

Abstract

Abstract

This document presents the syntax and semantics of UC++ Version 1.0.

UC++ is a parallel programming language designed as a superset of standard C++. For that reason, only the extensions implemented by UC++ are described in this document.

Copyright (c) 1994, The London Parallel Applications Centre (LPAC)

UC++ v1.0: Language Definition and Semantics

UC++ v1.0: Language Definition and Semantics

1 Introduction

UC++ is a parallel version of C++, being developed by the Department of Computer Science, University College London (UCL) and International Computers Ltd. (ICL) in a joint project organised by the London Parallel Applications Centre (LPAC).

This document describes UC++ 1.0, an implementation of UC++ intended primarily for computer architectures comprising multiple processors with separate memories.

1.1 UC++ Background

An object-oriented approach to parallelism has been used as the basis of UC++, as it is believed to provide a natural model of parallelism: objects encapsulate processes and member function calls encapsulate inter-process communication. To this end, UC++ introduces an active object semantic model to C++, combining parallelism with C++ objects, while making the minimum of syntactic extensions to C++.

An active object differs from a passive object (as found in standard C++) in that it additionally encapsulates a process, as well as state and operations. The process allows an active object to execute its member functions in parallel with the activity of the rest of the program. A parallel UC++ program, therefore, consists of a framework of active objects, each capable of executing their member functions in parallel with those of other active objects (see Figure 1).

In a typical UC++ program only a small number of objects will be active, providing the parallel structure of the program. The majority of objects will be standard passive objects, existing as members of active objects or in data structures managed by active objects. From this it can be seen that UC++ supports coarse to medium scale parallelism.

Communication between active objects is via the use of member function calls. UC++ supports both synchronous and asynchronous member function call between active objects. A synchronous call behaves in the same way as a standard C++ member function call, in that the caller is suspended until the called member function terminates. With an asynchronous call, the caller does not wait for the result of a function call to be returned and may continue processing. Asynchronous member function calls provide the mechanism for initiating parallelism in a UC++ program.

1.2 Aims and Goals

The broad aims of UC++ can be summarised as follows:

During the design of UC++ the aim of maintaining as much compatibility with C++ as possible has been a central issue. In particular, it was believed to be important to avoid creating a variant of C++ that had major syntactic differences and would require large amounts of existing C++ code to be re-written. The object-oriented approach to parallelism is particularly significant in this regard, as a well written object based C++ program is likely to maintain much of its structure when parallelised.

The version of UC++ described in the following sections provides a parallel version of C++ achieved by making a minimal number of extensions to C++, consistent with the aims and goals.

2 The UC++ Programming System

This section describes the underlying abstract machine and semantic model of UC++. Figure 2 shows the components of the abstract machine and the relationship to the active object model.

2.1 The UC++ Abstract Machine Model

The UC++ abstract machine provides a target system independent way of describing and reasoning about the behaviour of UC++ programs.

2.2 Virtual Processors

The UC++ active object model is based on an abstract machine consisting of a networked collection of virtual processing elements (i.e. processor/memory pairs), each supporting an active object. The model assumes that each virtual processor supports only a single active object and that the object has only a single thread of control.

The mapping of active objects to real processors depends on the process allocation mechanism of the operating system on the target system. By default, each request to create an active object will be satisfied by asking the operating system to create a new active objects process, if possible on a new processor (but taking into account any locations of active objects specified in a UC++ program).

2.2.1 Global Address Space

The abstract machine defines a global address space, where each address has a two part representation of the form: Processor_addr.Local_addr. Processor_addr identifies a particular processor. Local_addr is an address within the local address space defined by the memory directly available to the processor.

A processor can only access memory within its local address space. Any attempt to use an address where Processor_addr is not the address of the local processor will cause an error. This memory structure means that an active object can only access its own local memory and no other.

The physical representation of an address is dependent on the runtime system provided for a particular target architecture.

2.2.2 Virtual Processor Communications

All virtual processors are connected to a common communication subsystem. Packets of data may be passed between any two processors. Communication between active objects is built on top of this basic mechanism.

The actual communication medium is dependent on what is provided by the selected target system.

2.2.3 Target Architectures

The abstract machine presented by UC++ spans four major types of computer architectures (see Figure 3.):

  1. sequential machines such as a single UNIX workstation,
  2. multiprocessor computers with shared memory,
  3. multiprocessor computers with distributed memories, and
  4. distributed computers systems, connected by a suitable network.

By providing a suitable runtime library, UC++ programs can be supported on any of the architectures. To be executed on a different architecture, a UC++ program only needs to be recompiled in order to generate object code using the correct instruction set and to link the appropriate runtime libraries. No source code needs be changed.

2.3 Active Object Programming Model

This section outlines the UC++ active object model in the form seen by the programmer. A UC++ application consists of a number of active objects each supported by a single process. Active objects communicate by means of messages, which are produced, transmitted and processed by the UC++ runtime library.

2.3.1 Pointers

All pointers in a UC++ program appear and behave in the same way as in C++, except for the following:

Apart from these restrictions, pointers to active objects and pointers to any other values are not distinguished and standard C++ pointer syntax and semantics apply.

2.3.2 Remote Function Calls

Remote function calls, that is member function calls from one active object to another (or from a non-local passive object) are treated as messages between the virtual processors. Each processor maintains a queue which is processed in order by the active object. Once running a member function must complete and terminate before the next message in the queue will be processed.

A remote function call may be made asynchronously to an active object, so the caller does not wait for a result. An asynchronous call is the mechanism by which parallel behaviour is started.

2.3.3 Parameter Copying

When an active object member function is invoked, the caller and called object do not share a common address space, do not need to reside on the same system and may not even be executing in the same hardware architecture. For these reasons, a modified form of parameter passing is used, called pass by copy.

Pass by copy differs from normal C++ parameter passing in two ways:

i) The parameters are serialised i.e. transformed into a stream of printable characters by the UC++ runtime library and formed into a message. This message is transmitted to the target process and the parameters deserialised before the target function is called.

ii) Reference and pointer parameters within a called function refer to copies in the address space of the target active object, hence any modification of this data will have no effect on the data visible to the caller.

Pass by copy performs serialisation by means of the input and output operators '>>' and '<<' defined in the streams library. The streams library defines suitable functions for primitive data types only. The application developer is expected to provide appropriate functions to serialise application specific data types.

2.3.4 Application Termination

Active objects are normally terminated as a result of the delete operator. The implicit destructor is executed synchronously, which gives a guarantee on return, that all processing by the target object is complete.

When the application's main function returns, the runtime system will terminate any remaining active objects. It is preferable to avoid this action, as termination will occur in an arbitrary order without regard to any dependencies between active objects.

3 Language Elements

3.1 Creating Active Objects

An active object can be created using the UC++ keyword 'activenew'. The syntax is similar to the C++ keyword 'new'.

activenew class-id[(constructor-parameters)] [on resource-id]
When executed, this expression creates a virtual processor and constructs an active object in that processor. The address of the active object is returned. This expression may be used in any context where an object address is expected. Typical uses are:

i) the initialisation value in a pointer declaration,

ii) the right-hand of an assignment to a pointer.

3.2 Destroying Active Objects

An active object is removed using the delete operator as in C++.

delete Pointer; // Pointer may point at an active
// or a passive object or primitive data
Unless the pointer points at an active object, delete behaves exactly as in C++.

Where the pointer points at an active object, the effect is similar: the destructor, if defined, is called synchronously and the virtual processor containing the object is terminated. Any resources associated with the object are released, these include any local data or passive objects. Any files opened will be closed. In the case of a passive object, unless a destructor took appropriate action, store would not be released and files would remain open.

3.3 The On Clause

The 'on resource-id' clause provides a means to determine resources used by each new object. 'resource-id' may be either a integer value or a null terminated string. The effect of this clause is implementation dependant. If this clause is omitted, the effect is the same as if 'on 0' had been supplied.

3.3.1 Active Object Member Function Calls

An active object member function may be called either synchronously or asynchronously. In a synchronous call, the caller is suspended until the function returns (with or without a result). In an asynchronous call, the caller continues execution at the same time as the called function. This is the mechanism whereby parallelism is achieved.

The normal C++ syntax is used where a synchronous call is required. Implicit member function calls i.e. of constructors, destructors and operators are made synchronously.

An asynchronous call is specified by the asynchronous function call operator (@@). The effect of making an asynchronous call of a member function which returns a result is not defined. (@@) may be used to indicate asynchronous calls of constructors and destructors.

Examples:

X* p = activenew X( a, b, c ); // implied synchronous call X::X( a, b, c )
r = p->f( i, j, k ); // synchronous call r = X::f( i, j, k )
p->g(@ p, q, r @); // asynchronous call of X::g( p, q, r )
delete p; // implied synchronous call X::~X()