Previous Table of Contents Next


10.4.4.6. The Main Multitask Program

Here is the main program Show_Tasks.

    1 with Ada.Text_IO;
    2 with HB.Random_Task;
    3 with HB.Screen;
    4 procedure Show_Tasks is
    5
    6   task type SimpleTask (Message: Character;
    7                         HowMany: HB.Screen.Height;
    8                         Column:  HB.Screen.Width) is
    9     entry StartRunning;
   10   end SimpleTask;
   11
   12   task body SimpleTask is
   13     Nap: HB.Random_Task.RandomRange;
   14   begin
   15     accept StartRunning;
   16     for Count in 1..HowMany loop
   17       HB.Random_Task.Randomizer.GiveNumber(Result=>Nap);
   18       HB.Screen.Manager.Write(
   19         Where => (Row => Count, Column => Column),
   20         Item => Message & “ nap”
   21                         & Natural’Image(Nap) & “ secs”);
   22       delay Duration(Nap);
   23     end loop;
   24   end SimpleTask;
   25
   26   Task_A: SimpleTask
   27     (Message => ‘A’, HowMany => 5, Column => 1);
   28   Task_B: SimpleTask
   29     (Message => ‘B’, HowMany => 7, Column => 21);
   30   Task_C: SimpleTask
   31     (Message => ‘C’, HowMany => 4, Column => 41);
   32
   33 begin -- Show_Tasks
   34
   35   HB.Screen.Manager.ClearScreen;
   36   Task_B.StartRunning;
   37   Task_A.StartRunning;
   38   Task_C.StartRunning;
   39
   40 end Show_Tasks;

In this main unit, I declare the three subtasks. I could as easily have enclosed these in a package but chose to illustrate tasks declared in a main program. Lines 6-10 declare a task type SimpleTask. This implies that I can declare task objects; in fact, I do so in this program in lines 26-31. Ada tasks are, in fact, a form of active concurrent objects.

Each task declared in a program is activated (starts running) just after control reaches the begin of the block in which it is declared; the order of activation is not defined by the language. Each task declared in a package is activated when that package is elaborated, that is, just before control passes to the main procedure. Here, as each task of type SimpleTask is activated, the actual parameters (strictly speaking, discriminant values) for Message, HowMany, and Column are passed to it. Message indicates the constant message to be displayed in each cycle, HowMany indicates the number of cycles this task is to run, and Column indicates the screen column in which to display its message.

Because an Ada task is activated implicitly based on the enclosing program’s block structure, it is sometimes necessary to inhibit the task from doing any work until this “start button” is “pressed” by an entry call. To illustrate how this is done, SimpleTask also provides one entry, StartRunning.

Lines 12-24 show the body of the task type. When task objects are declared, each one has (in effect) a copy of this code. Each object may be mapped to an OS-level thread, but the precise mapping depends, of course, on the nature of the underlying OS support. The point is that the application programmer generally has no need to worry about this; the Ada implementation takes care of it just as it takes care of all the other platform dependencies such as memory structures, floating-point arithmetic, and so on.

I choose one task object arbitrarily and follow the execution expressed in the task body. Line 15 says

   accept StartRunning;

Upon reaching an accept, a task waits at the accept until the corresponding entry is called by another task. The task is put into a suspended or blocked state, which allows other tasks sharing the same CPU to run. The corresponding entry calls are issued here in lines 36-38. If the start button is never pressed, the task waits forever. I show in the package bodies how to guard against this eventuality.

Lines 16-23 are a simple for loop that runs for the desired number of cycles, according to the task’s HowMany parameter. The task calls for a random number (line 17) and then formats and displays a line via the screen manager (lines 18-21). Finally, line 22,

   delay Duration(Nap);

causes the task to sleep (suspend, block) for the desired number of seconds. The type conversion Duration(Nap) is required because the delay period must be of type Duration. Note that the nap length—delay period—is recomputed in each cycle, based on the random number. I have used a relative delay here, that is, the delay is relative to the current time. Ada also provides an absolute delay until, whose argument is a Time value.

It is important to understand that the Ada standard provides that a task becomes ready at the expiration of a delay period. This means that the task is not blocked and starts running again when it gains control of a CPU. The standard cannot guarantee that a CPU is available at precisely the right instant; this obviously depends on contention with other tasks in the program, or, in a multiprogramming environment, on contention with unrelated programs.

This is a simple example of the general concurrent-programming problem of ensuring that a cyclic process meets its deadline. In real-time system design, this is an important subdiscipline. It is certainly helpful to have clear and platform-independent concurrency constructs in the coding language, but these do not—cannot—substitute for careful analysis and design.


Previous Table of Contents Next