Dynamic Allocation of Tasks in occam3 - CiteSeerX

9 downloads 1585 Views 70KB Size Report
this case the plan manager can call the procedure modify.vlcb itself, having ..... [6, 1, Out]. 6. Merge. 3. [4, 2, In]. [5, 2, In]. [7, 0, Out]. 7. Destination. 1. [6, 2, In] ... PW Thompson and PH Welch (eds), Networks, Routers and Transputers, IOS Press,.
To be presented at WTC'94 Como Italy September 1994.

Dynamic Allocation of Tasks in occam3 Jon Kerridge* and Patrick Nixon+ *Department of Computer Science, The University, Sheffield, S10 2TN +Department of Computing, Manchester Metropolitan University, Manchester, M1 5GD [email protected], [email protected]

Abstract In many application environments there is a need to able to allocate resources dynamically. The occam programming language has no concept of such dynamic allocation. This paper shows how dynamic allocation can be incorporated into occam without sacrificing the benefits which are obtained using static allocation strategies. This capability has been achieved by a simple extension to occam3 to include a TASK mechanism which is defined using existing occam3 features. The main component of the dynamic mechanism is a library which manages the allocation and de-allocation of tasks. The paper includes an example showing how the dynamic allocation mechanism is employed.

1. Introduction The occam programming language is inherently a static language that does not permit the dynamic allocation of data structures and/or processes. From the philosophical point, where real-time systems are concerned, this is a justifiable position. However, from other viewpoints this position is less tenable. The advent (hopefully) of T9000/C104 networks makes it possible to dynamically connect process instances [1] to construct the execution graph of a network of processes which solve some data flow styles of computation. This paper takes that concept one stage further and suggests ways in which the occam computational model can be slightly extended to allow dynamic creation of tasks (processes). These tasks can then be connected by channels either to other dynamically created tasks or to process instances that are declared statically as part of the complete system. Software systems are often embedded in machinery which is inaccessible to the programmer. So after the system has been started, the software must operate for a long period of time without interruption. In certain cases it may be desirable to update or change the operation of the software in situ, for instance consider the case of a satellite system which must operate hundreds of miles above the earth's surface and cannot easily be recalled to earth for routine maintenance and upgrades to be performed on software. The occam language has been specifically designed to support real-time and embedded systems. Yet because of its static nature it is not a simple task to deal with the problems encountered in situations where dynamic reconfiguration or change is needed. In fact, very few languages actually provide suitable constructs for this type of activity [2]. The remainder of the paper describes the system architecture, starting in the next section with an architectural overview. This is followed by a description of the module used to occam is a registered trade mark of the INMOS Group of Companies Limited

allocate tasks, which is the key component of the architecture. We then describe the way in which execution plans are invoked as a set of tasks, we do not concern ourselves with how such a plan is created. We then discuss the structure of the task library itself which is followed by a description of the structure of an individual task. The description of the task library is completed by a discussion of the requirements of the processes within the task library which manage the resources (memory and channels) of the library. We then give an example of how the system would function for a simple execution plan, before drawing some conclusions. 2. Architectural Overview The proposed mechanism is to introduce the concept of a TASK which is a collection of process declarations, namely; INITIAL, RESOURCE and FINAL[3]. A TASK may only have channel parameters together with a parameter which is a unique identifier of the task instance. An occam3 LIBRARY is then defined which contains such TASK definitions together with the declaration of a memory vector and a vector of channels which have been allocated to virtual link control blocks (VLCB) [4]. The library contains within its definition processes that are used to allocate task instances on an as-needed basis. The library also contains processes which recover the memory and channels once a task instance has been terminated. Finally, the library contains a MODULE called TASK.ALLOCATOR, which is exported to processes wishing to incorporate such dynamic tasks into their structure. TASK.ALLOCATOR provides a channel based interface for a process which allocates an execution plan. The definition of a task library presumes that task instances can be invoked from any other processor. Each task library can only define and allocate instances of tasks declared within it. The number of instances of a particular task within a library instance is limited only by the available memory space and having sufficient capacity in the channel vector. Thus we have created a mechanism which permits the dynamic creation of processes but which still retains the basic philosophy of static memory allocation and hence predictable performance. A previous paper [1] described in detail, how process instances could be dynamically connected using the technique of header modification in a VLCB. We shall briefly review this technique and then discuss the underlying system level interactions required to support the TASK concept. A virtual link between two processes on different transputers requires each processor to maintain a VLCB, one field of which is called the header. A virtual link provides a bidirectional, two channel connection between the processes. When a communication is initiated between the processes; the outputting process, by utilising the Virtual Channel Processor (VCP) in the T9000[4], obtains from the VLCB on its transputer the header which is used to direct the message through the C104 communication network to the virtual link in the receiving or inputting process. The inputting process then reads the communication and sends an acknowledgement back to the outputting process. In order to direct the acknowledgement correctly a header is retrieved from the receiving processor's VLCB which contains the header used to communicate with the sending virtual link. Thus for two processes to communicate using a virtual link all that is required is that the headers in the VLCBs associated with each processes' virtual link contain the correct header information to reference the other's VLCB. In the simplest case each VLCB is allocated a unique header value. Other more complex VLCB labelling mechanisms can be used which involve the concept of header deletion [5]. This aspect will not be considered in this paper other than to note that the headers used can be of either type without alteration to the underlying design

principles. Thus to achieve dynamic connection between processes or tasks all that is required is a means of altering the header values in the VLCBs as necessary. The memory space of a T9000 transputer has many different address spaces mapped onto it, two of which are the channel addresses which are identified by channel numbers and the space allocated to VLCBs (Figure 11.6, page 201, in [4]). There is a simple relationship between channel number and the VLCB to which it relates. In fact a VLCB relates to both an input and an output virtual channel pair. The first virtual channel pair are numbered 16 and 17 (in, out respectively). The remaining virtual channel pairs are allocated in sequence. A simple numerical expression gives the address of the 32 byte VLCB. In addition, the VCP contains registers which enable the calculation of the header label which will result in messages being sent to a particular virtual input channel. Recall that a VLCB has to hold the header for messages which are to be output. However on a particular processor we will need to know the channel number of a virtual channel pair and the header which addresses that virtual channel pair. In due course when channels from different tasks are connected it will be necessary to modify the VLCBs of the respective virtual channel pairs so that they can send and acknowledge messages between themselves. To this end we assume the existence of a number of procedures and functions which manipulate channel numbers and headers. These will be identified as the architecture is explained. Destination Processes

Plan Manager get.task

Destination Processes

P2

P1

Plan Manager instance of TASK.ALLOCATOR

instance of TASK.ALLOCATOR

get.task

Source Processes

Source Processes

connect.task TASK LIBRARY SHARED CONNECTION connect.task SHARED FINISH.TASK end.task []BYTE memory []CHAN OF ANY vlcb.chans TASK DECLARATIONS MODULE TYPE TASK.ALLOCATOR ( )

Diagram 1 Process Architecture of the Task Allocation Mechanism Diagram 1 gives an outline of the architecture in which two transputers (P1 and P2) each contain an instance of the module TASK.ALLOCATOR, which allows them to communicate with the TASK LIBRARY, using the shared channel connect.task. The channel connect.task

is declared within the library and can only be accessed by instances of TASK.ALLOCATOR. Each instance of the TASK.ALLOCATOR also defines an INTERFACE which contains the definition of the channel get.task. A plan manager process in each processor is given the execution plans that are to be created. This plan is presumed to connect processes declared within the processors P1 or P2 with tasks from the TASK LIBRARY. These local processes are designated Destination and Source processes. It is presumed that the plan manager is able to obtain the channel number and headers of any channel which is local to the plan manager. Within the TASK LIBRARY is declared the memory BYTE vector which will hold the allocated tasks and also the vector of channels, called vlcb.chans, which can be allocated as needed by tasks. These allocatable channels have been declared with the anarchic protocol ANY. This will be resolved when the tasks are invoked because it is presumed that channels which are connected as a result of the task allocation mechanism will have the same protocol and thus the correct protocol checks can be invoked. 3. Task Allocator Module The MODULE TASK.ALLOCATOR provides the glue between the plan manager process and the task library. It converts the communications on get.task from the plan manager to the form required on connect.task used by the task library. The reason for this conversion mechanism arises mainly because the TASK.ALLOCATOR has to CLAIM access to the shared channel connect.task, thereby allowing many plan manager processes to gain access to the task library. The structure of the module is shown in the following code fold. The protocols and data declarations will be discussed when the task library is described. {{{ MODULE TASK.ALLOCATOR declaration MODULE TYPE TASK.ALLOCATOR () INTERFACE REQUEST.TASK get.task : TO [max.connections] CHANNEL.NUMBER chan.nos : [max.connections] HEADER hdrs : REASON failure.code : CHANNEL.NUMBER c.no : HEADER hdr.val : INT n : TASK.NAME t.name : WHILE TRUE get.task[request] ? CASE ... request a new task ... connect a channel : }}}

The module accepts requests from the plan manager either to allocate a new task or to connect channels between tasks that have already been allocated. The architecture presumes that a plan manager will first allocate all the tasks that are required and then undertake the connection of the channels. The mechanism for requesting a new task is shown in the following code fold. {{{ request a new task request.task; t.name CLAIM connect.task SEQ connect.task[request] ! get.task; t.name connect.task[reply] ? CASE not.allocated; failure.code get.task[reply] ! not.available; failure.code allocated; n::chan.nos; n::hdrs get.task[reply] ! available; n::chan.nos; n::hdrs }}}

The input from get.task is converted into a CLAIM on connect.task and the name of the task is passed to the task library. The task names are exported as an enumerated union and thus do not need to be the same as the actual name of the task which can remain private to the

task library. The module receives a reply from the task library either indicating unsuccessful allocation, or confirming that the task has been allocated and the numbers of the channels and their associated headers are also returned. These will be needed by the plan manager to effect the channel connections, as shown in the next code fold. {{{ connect a channel connect.channel; c.no; hdr.val CLAIM connect.task SEQ connect.task[request] ! attach; c.no, hdr.val connect.task[reply] ? CASE attached get.task[reply] ! connected not.attached; failure.code get.task[reply] ! not.connected; failure.code }}}

The plan manager simply sends the channel number and the header value which is to be placed in the associated VLCB. These are passed to the task library where the modification to the header is effected. 4. Plan Manager Process The plan manager imports from task_library the exported declarations, including the definition of the TASK.ALLOCATOR module. This is subsequently instanced as talloc. The plan manger obtains an execution plan and from it determines which source and destination processes are to be used. This enables identification of the source and destination channel numbers and their associated header labels. The plan manager can then obtain each of the tasks and make the required channel connections. The plan manager process can be executed at any time and just waits to be sent an execution plan. {{{ plan manager IMPORT FROM task_library TASK.NAME, HEADER, REASON, CHANNEL.NUMBER, DATAPATH, TASK.ALLOCATOR, Out, In : DATA TYPE TASK.RECORD RECORD TASK.NAME task: INT connections : [max.connections][3] INT detail: : [max.tasks] TASK.RECORD execution.plan : INT Tasks: [max.tasks][max.connections] CHANNEL.NUMBER c.n : [max.tasks][max.connections] HEADER hdr : MODULE talloc IS INSTANCE TASK.ALLOCATOR () : WHILE TRUE SEQ ... obtain the execution plan and initialise Tasks ... determine source and destination processes ... for each source channel get channel number and header ... for each destination channel get channel number and header SEQ t = 0 FOR Tasks ... get each of the required tasks SEQ t = 0 FOR Tasks SEQ c = 0 FOR execution.plan[t][connections] ... make the connections between each of the processes }}}

A TASK.RECORD holds the information about a particular task which is then used in a data structure called execution.plan, which holds the required information about a set of task allocations. The required data comprises the name of the task, the number of channel parameters defined in the task. For each such channel parameter there is a detail vector which indicates the task to which the channel is connected, the channel of that task and finally the direction of communication. The task indicator is simply the subscript of the task description in the execution.plan array. The next code fold shows how the plan manager calls talloc to request tasks.

Initially, the request element of get.task is used to communicate the name of the desired task. It should be noted that we can easily differentiate source and destination processes from tasks which have to be allocated because all allocatable tasks have at least two channel parameters, whereas source and destination processes have only one channel parameter that needs to be connected. {{{ get each of the required tasks IF -- task is a source or destination process i.e. only one connection execution.plan[t][connections] = 1 SKIP TRUE INT n: REASON error.code: SEQ talloc[get.task][request] ! request.task; execution.plan[t][task] talloc[get.task][reply] ? CASE available; n::[ c.n[t] FOR n ]; n::[ hdr[t] FOR n ] IF execution.plan[t][connections] = n SKIP TRUE ... terminate generation of the execution plan not.available; error.code ... terminate generation of the execution plan }}}

Assuming that the task has been allocated then the plan manager receives the channel numbers and headers of the channels which have been allocated to the task. These channel numbers and headers are stored in two data structures called c.n and hdr respectively which are then used to make the inter-process channel connections, once all the tasks have been allocated as shown in the next code fold. {{{ make the connections between each of the processes IF execution.plan[t][detail[c]][2] = Out SKIP TRUE -- process only the input ends of each connection VAL INT task.id IS execution.plan[t][detail[c]][0] : VAL INT connect IS execution.plan[t][detail[c]][1] : REASON failure.code: SEQ {{{ connect the first end of the channel IF c.n[task.id][connect] < 0 -- connection to a process on the plan manager processor modify.vlcb ( ABS ( c.n[task.id][connect] ), hdr[t][c], failure.code ) TRUE -- connecting a channel on the processor supporting the library SEQ talloc[get.task][request] ! connect.channel; c.n[task.id][connect]; hdr[t][c] talloc[get.task][reply] ? CASE connected SKIP not.connected; failure.code ... terminate generation of the execution plan }}} ... connect the second end of the channel }}}

The execution.plan data structure is processed by looking at only the input end of any connection. It is presumed that the channel connection between tasks is bi-directional. Thus any channel connection which is an output can be ignored because there must be a matching input. The mechanism essentially requires that the channel number and header of each end of a channel are obtained and then the headers are placed in the VLCB of the other end of the channel. The only complication occurs when the connection is to a source or destination process channel which is presumed to be on the same processor as the plan manager. This is indicated by storing the negative value of the channel number in the data structure c.n. In this case the plan manager can call the procedure modify.vlcb itself, having taken the

absolute value of the channel number. The other end of the channel is connected in a similar manner, except that the connection is between c.n[t][c] and hdr[task.id][connect]. If the connection is to be made to an allocated task then the request for connection is communicated on the request element of the get.task channel. PROC modify.vlcb ( VAL CHANNEL.NUMBER cn, VAL HEADER hdr, REASON error )

The procedure modify.vlcb causes the header field of the VLCB associated with the channel number passed as the first parameter to be set to the value in the second parameter. 5. Task definitions Tasks are introduced as the new functional unit which is used to encapsulate processes which can be dynamically created or deleted. The instantiation, or creation, of such a task is performed by the task library which is described in section 6. The task consists of three parts an INITIAL, RESOURCE, and FINAL declaration. In addition to the mandatory task identification parameter, the only parameters allowed in a task definition are channels, thus all data is communicated by means of channels, to and from the task, once it has been instantiated. An outline of a typical task, in this case a join operation, is shown in the following code fold. {{{ Task definition for join TASK join ( VAL TASK.ID task.id, DATAPATH in1, in2, out1 ) ... Extra data declarations ... initial definition ... resource definition ... final definition : }}}

The characteristics of such a task are that it must, once it has been initiated: ensure that its channels are initialised; perform the required task; and finally terminate gracefully. The channel intialisation is done by the using the resetch instruction supported on the T9000 [4, p. 195], which is encapsulated in a procedure called reset.channel. The procedure takes a channel parameter and applies a resetch to the channel. PROC reset.channel ( CHAN OF ANY channel )

Since all this is a prerequisite to execution of the task, these operations are performed in an INITIAL construct. {{{ initial definition INITIAL SEQ reset.channel ( In1 ) reset.channel ( In2 ) reset.channel ( Out1 ) : }}}

The main actions of the task are performed within the RESOURCE part of the task. This simply loops until termination, performing the required task. In the case of the example this is a join on the data from channels In1 and In2, with the result being placed on channel Out1. All the functionality of a standard occam process is allowed here.

{{{ resource definitions RESOURCE INITIAL BOOL going IS TRUE : WHILE going ... Do join on data : }}}

When termination is required the task enters the FINAL part of the task. This makes use of an additional shared channel of type FINISH.TASK, called end.task. This shared channel is claimed by the task in the FINAL declaration and a request to terminate the task is made, with the unique identifier for the task supplied at this point. The task library replies indicating that termination is about to be performed. The task then performs no other actions; the task library does the cleaning up required. {{{ final definiton FINAL CLAIM end.task SEQ end.task[finish] ! stop.task ; task.id end.task[finished] ? CASE task.stopped SKIP : }}}

6. Task Library The control of the tasks is performed by a task library. A library is used since it can have internal state which is necessary if some service is to be provided [3]. By allowing internal state, the library can keep track of task identifiers and control memory or channel allocation and deallocation. The task library described does all these activities by providing memory and channel definitions. Thus it is clear that the static nature of the language can be maintained, since the maximum memory and channel requirements, of the library, can be determined at compile time. The library consists of two parts, an INITIAL and RESOURCE. The INITIAL is simply used to ensure that all varaibles, such as those controlling memory and channel spaces, are intialised before the task service is provided. The RESOURCE part of the library performs the key activitites servicing the shared channels connect.task and end.task and thus provides the functions get task, attach channels and stop task. The following code fold gives the library structure. The protocol and data type definitions are given in an appendix. The library structure is as follows. The library exports all the necessary data types, constants, and the module TASK.ALLOCATOR. The library is then defined with templates for each task provided. In the library skeleton provided, there are task templates for join, merge and copy. These templates define the tasks which are allocatable by the library. Following these templates local data structures are defined before the the main body of the library is declared. This body provides the key task handling routines in an INITIAL and RESOURCE declaration, and defines the MODULE TASK.ALLOCATOR, discussed previously in section 3.

{{{ task library EXPORT DATA TYPE TASK.NAME: DATA TYPE HEADER: DATA TYPE REASON: DATA TYPE NAME CHANNEL.NUMBER: CHAN TYPE DATAPATH: VAL INT Out: -- used in execution plan data structure VAL INT In: -- to indicate the direction of the connection MODULE TYPE TASK.ALLOCATOR (): FROM ... DATA TYPE DEFINITIONS ... PROTOCOL AND CHANNEL DEFINITIONS TASK JOIN (VAL TASK.ID task.id, DATAPATH in1, in2, out ) ... task declaration for a database join : TASK MERGE (VAL TASK.ID task.id, DATAPATH in1, in2, out ) ... task declaration for a database merge : TASK COPY (VAL TASK.ID task.id, DATAPATH in, out1, out2 ) ... task declaration for a copy process : [mem.size] BYTE memory: [chan.size] CHAN OF ANY vlcb.chans: SHARED CONNECTION connect.task : SHARED FINISH.TASK end.task : ... other task allocation data structures ... LIBRARY INITIAL declaration ... LIBRARY RESOURCE declaration ... MODULE TASK.ALLOCATOR declaration : }}}

The RESOURCE declaration grants access to the two shared channels of the library and responds to the claims made by the module TASK.ALLOCATOR and the FINAL declaration of a TASK respectively, as shown in the following code fold. {{{ LIBRARY RESOURCE declaration RESOURCE WHILE TRUE ALT GRANT connect.task connect.task[request] ? CASE ... get.task ... attach GRANT end.task ... stop task : }}}

6.1 Task Creation Consider the allocation, or creation, of a task. A series of operations must be performed in order to load the task into memory. The task must first create a unique identifier for the task, called task.id. This identifier is used for arbitration over the tasks by the library. Then the memory space and channel space must be allocated to the task. Finally the task can be loaded into memory and its execution can begin. {{{ get.task get.task ; t.name SEQ ... allocate unique task.id ... allocate memory ... allocate channel numbers and obtain headers ... load task or return error }}}

To allocate the memory three arrays are maintained. An array of task identifiers is used to store each unique task.id because the identifiers may not be in sequence due to other libraries executing in parallel. The index into this array, which is found by searching the array for the value of task.id, is used as the index into the remaing two arrays. The first holds a memory start address for each task and the second the last memory location used by the task. This

assumes the existence of a procedure to perform memory management which uses standard algorithms to allocate, deallocate, and reuse memory space. A final variable is used to indicate the current task.id being dealt with; current.task. {{{ Other task allocation data structures [max.tasks] INT task.id, start.address, end.address : [max.tasks][max.connections] CHANNEL.NUMBER chan.nos : [max.tasks][max.connections] HEADER hdr.vals : INT current.task: INITIAL REASON error IS [](NONE) : }}}

Allocating channel numbers and headers is performed by two functions called get.cn and get.hdr. The function get.cn accesses data structures maintained in the VLCB and by the VCP[7, page 161] which allows the get.cn function to obtain the unique channel number for each channel. This unique number is returned when a call is made to the function with a channel name supplied. CHANNEL.NUMBER FUNCTION get.cn ( CHAN OF ANY channel.name )

The function get.hdr takes a channel number parameter to retrieve the channel header which is used in a different VLCB to direct messages to the channel referenced by the channel number. HEADER FUNCTION get.hdr ( CHANNEL.NUMBER channel.number )

Once the desired task has been identified, the number of channel parameters can be determined. These channels are obtained from the vector vlcb.chans. In order to allocate these channels calls to get.cn and get.hdr are performed for each channel. It is not neccessary for these channels to be physicaly in sequence even if the channels will be accessed as a vector in the task. {{{ allocate channel numbers and obtain headers -- n holds the number of channel parameters for the task SEQ ... for each of n channels get.cn ... for each of n channels get.hdr }}}

Once the task has had a unique task.id, channel numbers and headers allocated , there is sufficient information to load the task into memory. This action is performed by the procedure load.task. This is the last action performed as a consequence of the get.task call. This procedure simply places the code for the task, identified by name, at the location in memory pointed to by start.address[current.task]. The amount of memory taken up is derived using SIZE applied to the task. A vector of channel numbers is passed as a parameter and contains all the channel numbers which have been allocated to the task. The size of this vector is bounded by max.connections. PROC load.task ( TASK name, INT start.address, INT size, []CHANNEL.NUMBERS chan.nums)

The following code fold shows how the task is loaded and the required information concerning channel numbers and headers returned to the plan manager process through the task.allocator module.

{{{ load task IF error = [](NONE) SEQ load.task (JOIN, start.address[current.task], SIZE JOIN,chan.nos[current.task] ) get.task [reply] ! available; n::[chan.nos[current.task] FOR n]; n::[hdr.val[current.task] FOR n] TRUE get.task [reply] ! not.available; error }}}

6.2 Channel Connection The other function of the shared channel connect.task is to allow channels to be connected on demand. This is achieved by obtained by modifying the VLCB so that the relevant entries are correct. The request to attach a channel is made with two arguments, channel number, c.no, and header value, hdr.val. The header value is changed in the VLCB by the procedure modify.vlcb so that all data to the channel is directed to the correct place. {{{ attach task attach; c.no, hdr.val SEQ modify.vlcb ( c.no, hdr.val, error ) IF error = [](NONE) get.task [reply] ! attached TRUE get.task [reply] ! not.attached }}}

6.3 Task Termination A task is terminated, or stopped, by monitoring the shared channel end.task. On receiving a request on the shared channel, the library terminates a task. Firstly it acknowledges the call to terminate from the task, thus allowing the execution of the task to stop. It then performs three actions. Firstly it stops the all the channels used by the task. It does this by using the task.id supplied in the communcation on end.task to identify all channels and use the stop.channel procedure. The stop channel procedure makes use of the T9000 instructions to stop a channel called stopch. It then deallocates the memory and channel space used by the channel. To ensure that the task does not have errant behaviour after it has been stopped, the deallocated memory and channel spaces are intialised to known, safe states. {{{ stop.task SEQ end.task[finish] ? CASE stop.task ; current.task end.task[finished] ! task.stopped ... stop channels ... deallocate memory space ... deallocate channel space }}}

7. An Example Allocation Destination 0 2 Merge 0

1 2 Join

2 Join 0

0

1 2 Copy

1

1

0 0 Source 0

0 Source 1

0 Source 2

Diagram 2 Data Flow Graph for A Database Query Diagram 2 shows the structure of a simple execution plan which might result in a database application [6]. The numbers on the connections between the tasks indicate the channel parameter of the task which is used to support the communication. Data is fed into the plan from three different sources. The data from Source 1 is first put through a Copy process which duplicates the input onto two output streams. These output streams are directed to two Join processes which take a second input directly from Sources 0 and 2. The outputs from the Join processes are then merged into a single output stream which is directed to the Destination process. The execution.plan data structure would hold the following information : Row 0 1 2 3 4 5 6 7

task Source 0 Source 1 Source 2 Copy Join Join Merge Destination

connections 1 1 1 3 3 3 3 1

details[0] [4, 0, Out] [3, 0, Out] [5, 1, Out] [1, 0, In] [0, 0, In] [3, 1, In] [4, 2, In] [6, 2, In]

details[1]

details[2]

[5, 0, Out] [3, 2, In] [2, 0, In] [5, 2, In]

[4, 1, Out] [6, 0, Out] [6, 1, Out] [7, 0, Out]

As a result of processing by the plan manager the following would be the contents of the data structures c.n and hdr, where it is presumed that negative channel numbers are channels on the processor upon which the plan manager is executing. All the channel numbers and headers are arbitrary and merely give an impression of sensible values.

c.n

hdr

-16 -18 -20 16 24 34 40 -40

128 129 130 256 260 264 268 140

18 26 36 42

20 28 38 44

257 261 265 269

258 262 266 270

If we consider the bold connection in Diagram 2, the corresponding values in each of the data structures has been highlighted. The connection will be processed when row 6 of the execution.plan is processed. From Row 6 detail[1] it will be found that there is a connection to Row 5 detail[2], which can be cross checked. Thus the VLCB for channel 42 has to contain the header 266 and correspondingly the VLCB for channel 38 has to contain the header 269, to effect the communication. The previous code fragments have ensured that this effect is achieved. 8. Conclusions This paper has shown the feasibility of slightly extending occam3 to facilitate dynamic process creation, instantiation, and termination on a T9000 architecture. Three features have been described: The nature of a task as a simple restriction of a process; the task library as the main controller and arbitrator for the creation of tasks; and the plan manager which enables a process plan to be constructed at runtime and initiated. The new construct, TASK, which has been introduced does not compromise the static nature of occam3. Task types are predefined, for instance the join , merge, and copy operations, and have to be designed into the system. The maximum size of memory and channel space is also predefined, and also the maximum number of tasks is implicitly specified by the constant max.tasks used in the vector and array declarations, which means that all the usual static checking can be performed. One interesting result of the paper is that it emphasises the need for shared channels and data types in the occam3 langauge, since the extensions made would not be possible without these constructs. A notable fact, is that the new construct can be more readily described as imposing a structure on programs to allow dynamic processes, rather than as an introduction of a new language construct. This is an important point since one main argument against this new facilitiy would be the resultant size and complexity of the final language definition. We hope to be able to investigate the implementation of the TASK concept in the near future, as soon as we are in receipt of T9000 / C104 systems. 9. Acknowledgements The original idea for this style of data flow architecture, for database applications, was proposed by Paul Murray, now working for Hewlett-Packard Laboratories, Bristol.

10. References 1.

JM Kerridge, Dynamic Allocation of Processes and Channels in T9000/C104 Networks Using occam3, Progress in Transputer and occam Research, R Miles and A Chalmers (eds), The Proceedings of WoTUG 94, UWE Bristol, IOS Press, Amsterdam, 1994.

2.

P Inveradi, F Mazzanti, Experimenting with Dynamic Linking with Ada, Software - Practice and Experience, Vol. 20(1), January, 1993.

3.

G Barrett, DRAFT occam3 Reference Manual, Inmos Ltd, 1992.

4.

Inmos Ltd, The T9000 Transputer Hardware Reference Manual, 1st edition, 1993

5.

MD May, PW Thompson and PH Welch (eds), Networks, Routers and Transputers, IOS Press, Amsterdam, 1993.

6.

P Murray, Semantic Correctness in the Specification, Translation and Parallel Implementation of SQL Queries, PhD Thesis, submitted to University of Sheffield, 1993

7.

Inmos Ltd, The T9000 Instruction Set Manual, 1st Edition, 1993.

Appendix {{{ DATA TYPE DEFINITIONS DATA TYPE CHANNEL.NUMBER IS INT: DATA TYPE HEADER IS [3]BYTE: DATA TYPE REASON UNION NONE (0) NO.MEMORY.SPACE, (1) NO.CHANNELS.LEFT : : DATA TYPE TASK.NAME UNION NONE (0) Join, (1) Copy, (2) Merge : : DATA TYPE TASK.ID IS INT: VAL INT Out IS 0: VAL INT In IS 1: }}}

{{{ PROTOCOL AND CHANNEL DEFINITIONS PROTOCOL R.TASK CASE request.task; TASK.NAME not.available; REASON available; INT :: [] CHANNEL.NUMBER; INT :: HEADER connect.channel; CHANNEL.NUMBER; HEADER connected not.connected; REASON : CHAN TYPE REQUEST.TASK RECORD CHAN OF R.TASK request, reply : : PROTOCOL C.TASK CASE get.task; TASK.NAME allocated; INT :: [] CHANNEL.NUMBER; INT :: HEADER not.allocated; REASON attach; CHANNEL.NUMBER; HEADER attached not.attached : CHAN TYPE CONNECTION RECORD CHAN OF C.TASK request, reply : : PROTOCOL DATASTREAM CASE ... DATASTREAM protocol components : CHAN TYPE DATAPATH RECORD CHAN OF DATASTREAM forward, reverse: : PROTOCOL F.TASK CASE stop.task ; INT task.stopped : CHAN TYPE FINISH.TASK RECORD CHAN OF F.TASK finish, finished: : }}}