Dealing with Linked Lists of Tasks
This example uses tasks with entries, hierarchical packages, and a protected
object
for a little spice.
Starting at the lowest level of abstraction, we have the specification for
the package
defining the simple tasks:
-----------------------------------------------------------------------
-- Simple repetitive task type
-----------------------------------------------------------------------
package Simple_Tasks is
task type Simple is
Entry Start;
Entry Stop;
end Simple;
end Simple_Tasks;
------------------------
-- The package body for Simple_Tasks contains the protected object as well
as
-- the completion of task type Simple.
with Ada.Text_Io;
package body Simple_Tasks is
-- Description/Usage: The protected object Counter maintains a
-- local counter for the tasks. Each task will call the procedure
-- Take_Number to obtain a value indicating its relative temporal
-- position.
--
-- Many instances of the task type Simple may be created, but there
-- will only be one instance of the protected object Counter.
-- All instances of the task type Simple must call the Take_Number
-- procedure after their Start entry is called.
--
protected Counter is
procedure Take_Number(Count : out Positive);
private
Tally : Positive := 1;
end Counter;
protected body Counter is
-- This procedure delivers the current tally value then
-- increments the tally value. It is assumed that this
-- procedure will not be called often enough to attempt to
-- exceed Positive'Last.
-- The exception Constraint_Error will be raised by the
-- runtime system should this ever occur, terminating the
-- program in a most ungraceful manner.
procedure Take_Number(Count : out Positive) is
begin
Count := Tally;
Tally := Tally + 1;
end Take_Number;
end Counter;
------------
-- Simple --
------------
task body Simple is
My_Count : Positive;
begin
-- Wait for some other task to call this task's Start entry.
accept Start;
Counter.Take_Number(My_Count);
Ada.Text_Io.Put_Line("Task" & Positive'Image(My_Count) &
" started.");
-- Suspend this task until some other task calls this task's
-- Stop entry.
accept Stop;
Ada.Text_Io.Put_Line("Task" & Positive'Image(My_Count) &
" stopped.");
end Simple;
end Simple_Tasks;
-----------------------
-- Next we have a child package of the package Simple_Tasks.
-- This child package defines a very simple linked list manipulation
-- mechanism for Simple_Tasks.
------------------------
-----------------------------------------------------------------------
-- List package for the simple tasks
--
-- This package is a child of the Simple_Tasks package, extending the
-- capabilities of that package without altering the parent package.
-----------------------------------------------------------------------
package Simple_Tasks.Lists is
-- Description/Usage: Preliminary (incomplete) definition of a
-- list type for tasks.
type List is private;
--------------------------------------------------------------------
-- Adds another task to the list of tasks
--------------------------------------------------------------------
procedure Lengthen(The_List : in out List);
--------------------------------------------------------------------
-- Stops all the tasks on the list and deallocates the memory used
-- by those tasks
--------------------------------------------------------------------
procedure Halt_Tasks(Task_List : in out List);
private
-- Description/Usage: forward declaration of the list element type
type List_Element;
-- Description/Usage: Completion of the definition of the List type.
type List is access List_Element;
-- Description/Usage: Completion of the definition of the
-- List_Element type. Being private, this type is not accessable
-- to the users of this package. It is used only within the
-- body of this package.
type List_Element is record
Thread : Simple;
Next : List := Null;
end record;
end Simple_Tasks.Lists;
-------------------------------------------
-- Of course, this package needs a body too.
------------------------------------------
with Ada.Unchecked_Deallocation;
package body Simple_Tasks.Lists is
----------------
-- Halt_Tasks --
----------------
procedure Halt_Tasks (Task_List : in out List) is
procedure Free is new Ada.Unchecked_Deallocation(
Object => List_Element,
Name => List);
Temp : List;
begin
while Task_List /= null loop
Temp := Task_List;
Task_List := Task_List.Next;
-- Call the Stop entry for the task.
Temp.Thread.Stop;
-- Make sure the task is terminated before deallocating it.
while not Temp.Thread'Terminated loop
delay 0.001;
end loop;
-- Deallocate the List_Element containing the task
Free(Temp);
end loop;
end Halt_Tasks;
--------------
-- Lengthen --
--------------
procedure Lengthen (The_List : in out List) is
Temp : List;
begin
-- simple linked list operation. Add the first element
-- if the list is empty
if The_List = null then
-- Allocate the new List_Element
The_List := new List_Element;
-- Call the Start entry for task in the newly allocated
-- List_Entry
The_List.Thread.Start;
else
-- Add to the end of the list if it is not empty.
Temp := The_List;
-- This operation gets longer as the number of tasks
-- increases.
while Temp.Next /= null loop
Temp := Temp.Next;
end loop;
-- Allocate the new List_Element
Temp.Next := new List_Element;
-- Call the Start entry for task in the newly allocated
-- List_Entry
Temp.Next.Thread.Start;
end if;
end Lengthen;
end Simple_Tasks.Lists;
-----------------------------------
-- Finally, we have the procedure used to test the
-- Simple_Tasks.Lists package.
-----------------------------------
-----------------------------------------------------------------------
-- PROCEDURE NAME: Count_Tasks
--
-- PURPOSE: This procedure is the driver for testing the task list
-- operations defined in the package Simple_Tasks.Lists.
--
-----------------------------------------------------------------------
with Simple_Tasks.Lists; use Simple_Tasks.Lists;
with Ada.Text_Io;
procedure Count_Tasks is
-- Description/Usage: A list of tasks, starting with an
-- empty list.
--
My_List : List;
begin
-- Create a number of tasks and add them to the list of tasks
for Threads in 1..60 loop
Lengthen(My_List);
end loop;
-- Allow all tasks in the list to have their turn executing
delay 0.0;
-- Stop all tasks on the list and deallocate the list elements
Halt_Tasks(My_List);
Ada.Text_Io.Put_Line("All tasks stopped and deallocated.");
end Count_Tasks;
Contributed by: James S. Rogers
Contributed on: December 30, 1999
License: Public Domain
Back