Lecture Notes in Computer Science Vol 191, Springer-Verlag (1985)
Total Page:16
File Type:pdf, Size:1020Kb
ACKNOWLEDGEMENT We are grateful to Ian Pyle for his valuable comments on a draft of this book. APPENDIX A Task Initialisation in Highly Parallel Systems In many applications it is necessary for a task to know its identity so that, for example, resource allocation mechanisms can be implemented reliably. Unfortunately as an Ada task cannot be initialised at the time of creation, a rendezvous must be used to pass identification data to those tasks that require it. With multiprocessor systems this initialisation phase can be a source of inefficiency as it is, essentially, a sequential activity. In this appendix we present Bums' algorithm 42 for initialising tasks in highly parallel systems. Each task is presented in an initial rendezvous with a range (B..T) of identities of uninitialised tasks. The task initialises its own identity to the top element (T) of the range. Following the rendezvous, the task splits its given range in two and passes the top half to another uninitialised task. It then repeats the process on the bottom half; i.e. it splits it in two, passes the top half on, and so on. Eventually the bottom half will consist of a single identity (top = base) and, having passed this identity on, the task will have completed its initialisation role. It is easy to see that the time taken to initialise all the tasks depends logarithmically on the number of tasks. Thus over ten thousand tasks can be initialised within 14 rendezvous times. 108 A REVIEW OF ADA TASKLNG procedure main is max : constant := ~oo; --number of tasks to be created subtype task_range is integer range 0 .. max; task type TK is entry initialise(B,T : task_range); end TK; set : array(task_range) of TK; base, new_base : task_range; top : integer range task_range~first-! .o task_range'last; task body TK is my_number : task_range; -- other declarations begin declare base,new_base : task_range; top : integer range task_range'first-i oo task_range'last; begin accept initialise(B,T : task_range) do my_number := T; base := B; end initialise; top := my_number - i; while top >= base loop new_base :: (top + base)/2; set(top).initialise(new_base,top); --top half of remaining group top := new_base - I; end loop; end; end TK; APPENDIX A 109 begin top :: task_range'last; base := task_range'first; while top >= base loop new_base := (top + base)/2; set(top).initialise(new_base,top); --top half of remaining group top := new_base - i; end loop; end main; APPENDIX B Asynchronous Agents Normal inter-task communication is synchronous. This appendix contains generic packages to implement asynchronous send and receive operations. It also gives an examples of their use. First the asynchronous send: generic type mess is private; with procedure destination(m : mess); package no_wait_send is task type agent is entry forward(m : mess); end agent; type agt is access agent; end no_wait_send; package body no_wait_send is task body agent is temp : mess; begin accept forward(m : mess) do temp := m; end; destination(temp); end agent; end no_wait_send; The generic package provides a task which stores a single message (for the client that created it) and passes on this message by calling the procedure destination which is 112 A REVIEW OF ADA TASKING provided as a generic parameter. A simple example of use is a main program calling (indirectly) the entry 'request' in the task 'server': with no_wait_send; procedure main is type message is new integer; task server is entry request(m : message); end server; package messenger is new no_wait_send(mess => message, destination => server.request); use messenger; phidippides : agz := new agent; m : message; task body server is separate; begin -- obtain value for m phidippides.forward(m); -- continue execution end main; The following package implements an asynchronous receive. APPENDIX B 113 generic type mess is private; package mailbox is task type agent is entry deposit(m : mess); entry collect(m : out mess); end agent; type agt is access agent; end mailbox; package body mailbox is task body agent is temp : mess; begin accept deposit(m : mess) do temp := m; end; accept collect(m : out mess) do m := temp; end; end agent; end mailbox; With this generic package a task type is provided that has two entry calls, one for the message to be deposited and the other to allow the client task to collect the message when it is ready to do so. Use in a server package is illustrated below. serverl ' call "~-Ic°ntr°ll Iag ent ] A server task will call the procedure transaction in order to pass data to control and receive the results. The transaction procedure, however, calls up a mailbox agent to handle the return data. The control task therefore receives the input data and an "address" (access variable) to where the reply should be sent. 114 A REVIEW OF ADA TASKING package server is type input_data is new integer; type output_data is new integer; procedure transaction(I :input_data; 0 : out output_data); end server; with mailbox; package body server is package mbox is new mailbox(mess => output_data ; use mbox; task control is entry call(I : input_data; A : agt); end control; procedure transaction(I:input_data;0:out output_data) is A : agt := new agent; begin control.call(I,A); -- give input data to the -- control task together with -- a mailbox agent for the -- return data A.coi!ect(O) ; -- wait for data to be -- ready in mailbox end transaction; task body control is temp_I : input_data; temp_0 : output_data; temp_A : agt; begin accept call(I : input_dana; A : agt) do temp_I := I; temp_A := A; end; -- produce temp_0 from temp_I temp_A.deposit(temp_0); -- when appropriate end control; end server; APPENDIX C Implementation of a Reliable Transaction This appendix contains a generic package that allows a reliable two-rendezvous transaction to be programmed in Ada. The reliability comes from two features: (a) The server task returns data to the client (during the second rendezvous) via an agent. The failure or abortion of the client does not therefore affect the server. (b) A pool of reusable agents is used to overcome the storage exhaustion problem (see section 4.1.2). The generic package has the specification shown below. The generic parameters provide a type for the re'turn data and a constant giving the size of the pool. For simplicity, this example uses a fixed size pool of agents. A dynamic pool is also possible, in which case the constant parameter would give the maximum size of the pool. 116 A REVIEW OF ADA TASKING generic type mess is private; size_of_pool : integer := 16; package agent_pool is type agent is private; procedure deposit(agt : agent; m : mess); procedure collect(agt : agent; m : out mess); procedure allocate(agt : out agent); procedure free(agt : in out agent); NO_AVAILABLE_AGENT : exception; -- raised by allocate private task type agent_t is entry deposit(m : mess); entry collect(m : out mess); end agent_t; type agent_p is access agent_t; type agent is record in_use : boolean :: FALSE; ptr : agent_p := new agent_t; end record; end agent_pool; The generic package provides four procedures: two for obtaining and releasing an agent, and two for communication with the agent. An agent can therefore be used a number of times by a client before being returned to the pool. The agent itself has the form of an asynchronous receiver (see Appendix B). Also defined within the package is an exception that is raised if there is no free agent available. The body for the generic package is as follows. (Note that all agents and the agent manager terminate when required to do so.) APPENDIX C 117 package body agent_pool is task manager is entry allocate(agt : out agent); e~ry free(agt: in out agent); end manager; procedure deposit(agt : agent; m : mess) is begin agt.ptr.deposit(m); end deposit; procedure collect(agt : agent; m : out mess) is begin agt.ptr.collect(m); end collect; procedure allocate(agt : out agent) is begin manager.allocate(agt); end allocate; procedure free(agt : in out agent) is begin manager.free(agt); end free; task body agent_t is temp : mess; begin loop select accept deposit(m : mess) do temp := m; end; or terminate; end select; accept collect(m : out mess) do m := temp; end; end loop; end agent_t; ]8 A REVIEW OF ADA TASK~G task body manager is subtype pool_size is integer range i.. size_of_pool; agent_pool : array (pool_size) of agent; found : boolean begin loop loop -- first all outstanding calls -- to free are processed select accept free(agt : in out agent) do agt.in_use := FALSE; end free; else exit; end select; end loop; begin select accept alloeate(agt : out agent) do found := FALSE; for i in pool_size loop if not agent_pool(i).in_use then agent_pool(i).in_use :: TRUE; agt := agent_pool(i); found := TRUE; exit; end if; end loop; if not found then raise NO_AVAILABLE_AGENT; end if; end allocate; or accept free(agt : in out agent) do agt.in_use := FALSE; end free; or terminate; end select; exception when NO_AVAILABLE_AGENT => null; end; end loop; end manager; end agent_pool; APPENDIX C 119 To illustrate the use of this generic package the simple server task employed in Appendix B is implemented (reliably!): package server is type input_data is new integer; type output_data is new integer; procedure transaction(I:input_data;0:out output_data); end server; with agent_pool; package body server is package reliable_agents is new agent_pool(mess => output_data); task control is entry call(I:input_data;A:reliable_agents.agent); end control; procedure transaction(I:input_data;0:out output_data) is A :reliable_agents.agent; begin reliable_agents.allocate(A); control.call(I,A); reliable_agents.collect(A,O); reliable_agents.free(A); end transaction; task body control is temp_l : input_data; temp_0 : output_data; temp_A : reliable_agents.agent; begin accept call(I:input_data;A:re!iable_agents.agent) do temp_I := I; temp_A := A; end; reliable_agents.deposit(temp_A, temp_0);--when appropriate end control; end server; APPENDIX D Using Families For Handling Priority Requests In Section 4.2.6.3 a prioritised select was implemented to give preference to one entry over another in a two accept select statement.