[net.sources] SRI Ada tasking benchmarks - part 1 of 3

joe@petsd.UUCP (Joe Orost) (12/12/84)

<>
See announcement in net.lang.ada.
------------------------------CUT HERE------------------------------
#!/bin/sh
cat >README <<'------ EOF ------'



        PERFORMANCE TESTING OF SOME ADA PROGRAMMING CONTRUCTS

SRI is developing a packet switched network node, using the Ada
programming language, and using the SUN Microsystem processor board as
the target hardware, which contains a MC68000.  We have run some timing
measurements on specially written Ada test programs in order to
determine how to optimally use tasking, rendevous, selects, and
parameter passing, and also to make an early prediction on the
packet-per-second throughput of the system.

The test were compiled with the Telesoft Ada Compiler on a Diskless SUN
Workstation, running UNIX 4.1c bsd.  The workstation was configured
with 2 megabytes of local memory, and only a single user was logged in.

When the Run command is given to start the Ada program, it takes about
5 seconds for the run-time support environment to be loaded to the
diskless sun.  Therefore timing is not started until a prompt from the
program is answered by the user with a carriage return.  Timing is done
manually with a stopwatch.  The program may optionally turn on
printing, to check for deadlock situations, by answering the promt with
a 'y' - this of course slows down the program, and these runs should
not be used for performance measuring.

Most of the timings for each program were repeated 5 times, and the
variance in time was seldom more than a second.  Timings given are the
averages for multiple trials.

Following the times below is a summary of the program characteristics
and the conclusions drawn from the tests.

program         cycles          seconds
________________________________________
chain2          1000            3.57
chain5          1000            10.15
chain10         1000            19.66
chain20         1000            38.03

idle1           10000           29.46
idle5                           -
idle10                          -
idle20          10000           29.93

select2         1000            4.38
select2e        1000            4.38
select20        1000            8.42
select20e       1000            8.33

guard2          1000            4.28
guard2e         1000            4.22
guard20         1000            6.20
guard20e        1000            6.11
guard20t        1000            8.31
guard20et       1000            8.11

chain2n         10000           29.58
chain2pkt       10000           29.77
chain2ptr       10000           29.98

passarrys       10000           29.
passarryb       10000           29.
passinout       10000           29.

moretasks       1000            38.
moretasksl      1000            47.
moreselct       1000            128.
moreselctr      1000            130.

order31         100             28.
order31r        100             28.
order32         compiles without errors, but crashes when run
order100        compiles without errors, but crashes when run

DESCRIPTION OF TEST PROGRAMS AND RESULTS

CHAIN - TO DETERMINE OVERHEAD IN CONTEXTS SWITCHES BETWEEN TASKS
Each chain task, within each cycle of the loop, calls an entry in the
"next task" in a chain of tasks, the called entry contains a null
statement and returns, and the tasks then waits to be called by another
task at a similar entry of its own.  Thus each task is run in turn
dependent on its position in the chain.  Chains of length 2, 5, 10, and
20 tasks were compared after 1000 complete cycles around the chain.
Times recorded were
chain2          1000            3.57
chain5          1000            10.15
chain10         1000            19.66
chain20         1000            38.03
Dividing these times by the number of tasks in each test yields
respectively 1.78, 2.03, 1.96, and 1.90; dividing by the number of
cycles then indicates that each context switch (rendezvous) costs about
2 millisec.

IDLE - DETERMINE WHETHER IDLE TASKS IMPACT PERFORMANCE
A chain of length 2 as described above was cycled 10000 times, before
the cycles are started, some number of "idle" task are called at an
"init" entry and are then left waiting at a "never" entry which will
never be called.  The timings for 1 and 20 idle task are recorded below
idle1           10000           29.46
idle5                           -
idle10                          -
idle20          10000           29.93
Within the accuracy of the measurements, there is no difference
in the timings, which implies that there is no performance penalty
for increasing numbers of tasks waiting on a single entrys.

SELECT - DOES THE NUMBER OF SELECT CHOICES MATTER
One task calls a single entry of a second task 1000 times, but
the second task has a select statement encompassing some
number of alternatives. The test was done for 2 and 20
alternatives, with the desired entry being the first one
in the select list, and repeated for the desired entry
being at the end of the select list.
select2         1000            4.38
select2e        1000            4.38
select20        1000            8.42
select20e       1000            8.33
These results show that large select statements are costly.

GUARDS - DO GUARDS ON ENTRY STATEMENTS IMPACT PERFORMANCE
The select tests above were repeated with boolean guards placed
in front of all the entry choices.  In some cases, only the 
guard on the entry which would really be called was true, and
all of the other guards were false.  In other cases, all of the
guards were set to true.
guard2          1000            4.28
guard2e         1000            4.22
guard20         1000            6.20
guard20e        1000            6.11
guard20t        1000            8.31
guard20et       1000            8.11
Comparing these results with the previous tests, it appears that
the cost of using guards on select entrys is very small. 
A guard which evaluates to false apparently significantly reduces the overhead
of evaluating the guarded select.

PARAMETERS - WHAT IS THE IMPACT OF PASSING PARAMETERS IN RENDEVOUS
The following chain test were run passing "no" parameters, passing
a packet record as a parameter, and passing a pointer to a record.
chain2n         10000           29.58
chain2pkt       10000           29.77
chain2ptr       10000           29.98
The results show the there is no measurable cost in using entry parameters.

PARAMETER SIZE - DOES SIZE OF THE PASSED PARAMETER MAKE A DIFFERENCE
The above test was repeated with a parameters as follows. A "in"
small integer array of length 2, an "in" integer array length 32000,
and an "in out" integer array length 32000.
passarrys       10000           29.
passarryb       10000           29.
passinout       10000           29.
There is no observed cost in using large structures as parameters.

TASKS - IS IT BETTER TO HAVE LOTS OF LITTLE TASKS WITH SINGLE ENTRY CHOICES
        OR FEW BIG TASKS WITH MANY SELECT CHOICES
Some of the previous results would imply the use of many tasks.
In the "moretasks" tests, a master tasks calls each of 20 slave tasks,
each with a single entry. In "moretasksl" each task again has a single
entry, but it is embedded in a select statement for fair comparison
to the next test.  In "moreselct" a master task calls each of the 20
entrys in a single slave task, and the slave task has the 20 entrys
embedded in a large select statement.  In the "moreselctr" the 20
entrys are listed in the opposite order to which the master calls them.
moretasks       1000            38.
moretasksl      1000            47.
moreselct       1000            128.
moreselctr      1000            130.
The results suggest to use lots of tasks with few select choices.

ORDER - DOES ORDERING OF ENTRY CLAUSES IN A SELECT MATTER
The "moreselct" test was modified by increasing the number of
entry clauses to 100.  However it was discovered that a
select statement can only contain a maximum of 31 choices.
Then the program was run for 100 cycles. Another test was
run calling the entrys in the reverse of the select statement.
order31         100             28.
order31r        100             28.
order32         compiles without errors, but crashes when run
order100        compiles without errors, but crashes when run
No difference was determined, however if a large select clause
were permitted (100 entries) it may have suggested which ordering
was optimal.

SCHEDTEST - DETERMINE WHETHER THE ADA SCHEDULER MAY STARVE A TASK

A slave task with a two entry select statement is used independently by
three other tasks.  The test is run until the slave has been called
1000 times.  Two of the tasks call the first entry, and the third task
calls the second slave entry.  Each task, and the slave have print
statements to help determine which task is running.  The order and
relative frequency of the tasks printout will show whether any of the
task are starved or run more often than the others.  When the test was
run, it was seen that the three tasks alternately print their rendezous
annoucement once each.  Thus, none of the tasks were starved, and
rescheduling apparently occurs with the frequency of one rendezous.


CONCLUSIONS
The overhead of a rendevous or task context switch takes 1 - 2 millisecs.
The number of idle tasks waiting on uncalled entries, do not impact speed.
The number of entrys in a select significantly impacts selection speed.
Evaluation of "when" clauses is quick, and when false, prevent the
        long select evaluation time, speeding the system.
Passing parameters in rendezous is quick, and there is not much difference
        on parameter size or whether "in" or "in out".
The ordering of entrys in a select clause in not important.
To build an optimized system, use more tasks, each with less number
        of entries in select clauses, and use guards.

CAUTIONS
Array index are apparently limited to 32000 elements.
Selects may have no more than 31 possible entries.
 
TELESOFT COMPILER LIMITATIONS

The Telesoft Ada Compiler that we have used for performing these
benchmarks is not a complete implementation of the language.  Telesoft
is currently in the process of validating their full Ada compiler, and
we will then get an update with the full language implemented.  Some of
the deficiencies of the language, which affected our selection of
benchmarks and programming style, are generics, subunits, some pragmas,
representation specifications, tasks types, entry families, timed entry
calls and the calendar package, and the abort statement.

SUGGESTIONS FOR FURTHER ADA BENCHMARK STUDIES

Most of the test which we performed were concerned with determining how
to optimize task and select statement organization.  However, it is
important to understand many of the other facilites of the rich
language, some of which are not yet implemented in our compiler.
Particularly in our application of a real time packet switching node,
we should study the performance of representation specifications, low
level input/output, the timing facilities, aborts, and interrupt
handling.

------ EOF ------
ls -l README
cat >chain10.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;

        task head is
                entry give;
        end head;

        task link2 is
                entry give;
        end link2;

        task link3 is
                entry give;
        end link3;

        task link4 is
                entry give;
        end link4;

        task link5 is
                entry give;
        end link5;

        task link11 is
                entry give;
        end link11;

        task link12 is
                entry give;
        end link12;

        task link13 is
                entry give;
        end link13;

        task link14 is
                entry give;
        end link14;

        task link15 is
                entry give;
        end link15;

        task body head is
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 1000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link2.give;
                                accept give do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link2 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link2"); end if;
                                link3.give;
                        end loop;
                end link2;

        task body link3 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link3"); end if;
                                link4.give;
                        end loop;
                end link3;

        task body link4 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link4"); end if;
                                link5.give;
                        end loop;
                end link4;

        task body link5 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link5"); end if;
                                link11.give;
                        end loop;
                end link5;

        task body link11 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link11"); end if;
                                link12.give;
                        end loop;
                end link11;

        task body link12 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link12"); end if;
                                link13.give;
                        end loop;
                end link12;

        task body link13 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link13"); end if;
                                link14.give;
                        end loop;
                end link13;

        task body link14 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link14"); end if;
                                link15.give;
                        end loop;
                end link14;

        task body link15 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link15"); end if;
                                head.give;
                        end loop;
                end link15;


begin
        null;
end test;
------ EOF ------
ls -l chain10.ada
cat >chain2.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;

        task head is
                entry give;
        end head;

        task link1 is
                entry give;
        end link1;

        task body head is
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 1000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link1.give;
                                accept give do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link1 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link1"); end if;
                                head.give;
                        end loop;
                end link1;


begin
        null;
end test;
------ EOF ------
ls -l chain2.ada
cat >chain20.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;

        task head is
                entry give;
        end head;

        task link2 is
                entry give;
        end link2;

        task link3 is
                entry give;
        end link3;

        task link4 is
                entry give;
        end link4;

        task link5 is
                entry give;
        end link5;

        task link11 is
                entry give;
        end link11;

        task link12 is
                entry give;
        end link12;

        task link13 is
                entry give;
        end link13;

        task link14 is
                entry give;
        end link14;

        task link15 is
                entry give;
        end link15;

        task link21 is
                entry give;
        end link21;

        task link22 is
                entry give;
        end link22;

        task link23 is
                entry give;
        end link23;

        task link24 is
                entry give;
        end link24;

        task link25 is
                entry give;
        end link25;

        task link211 is
                entry give;
        end link211;

        task link212 is
                entry give;
        end link212;

        task link213 is
                entry give;
        end link213;

        task link214 is
                entry give;
        end link214;

        task link215 is
                entry give;
        end link215;

        task body head is
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 1000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link2.give;
                                accept give do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link2 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link2"); end if;
                                link3.give;
                        end loop;
                end link2;

        task body link3 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link3"); end if;
                                link4.give;
                        end loop;
                end link3;

        task body link4 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link4"); end if;
                                link5.give;
                        end loop;
                end link4;

        task body link5 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link5"); end if;
                                link11.give;
                        end loop;
                end link5;

        task body link11 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link11"); end if;
                                link12.give;
                        end loop;
                end link11;

        task body link12 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link12"); end if;
                                link13.give;
                        end loop;
                end link12;

        task body link13 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link13"); end if;
                                link14.give;
                        end loop;
                end link13;

        task body link14 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link14"); end if;
                                link15.give;
                        end loop;
                end link14;

        task body link15 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link15"); end if;
                                link21.give;
                        end loop;
                end link15;

        task body link21 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link21"); end if;
                                link22.give;
                        end loop;
                end link21;

        task body link22 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link22"); end if;
                                link23.give;
                        end loop;
                end link22;

        task body link23 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link23"); end if;
                                link24.give;
                        end loop;
                end link23;

        task body link24 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link24"); end if;
                                link25.give;
                        end loop;
                end link24;

        task body link25 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link25"); end if;
                                link211.give;
                        end loop;
                end link25;

        task body link211 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link211"); end if;
                                link212.give;
                        end loop;
                end link211;

        task body link212 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link212"); end if;
                                link213.give;
                        end loop;
                end link212;

        task body link213 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link213"); end if;
                                link214.give;
                        end loop;
                end link213;

        task body link214 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link214"); end if;
                                link215.give;
                        end loop;
                end link214;

        task body link215 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link215"); end if;
                                head.give;
                        end loop;
                end link215;


begin
        null;
end test;
------ EOF ------
ls -l chain20.ada
cat >chain2n.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;

        task head is
                entry give;
        end head;

        task link1 is
                entry give;
        end link1;

        task body head is
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 10000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link1.give;
                                accept give do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link1 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link1"); end if;
                                head.give;
                        end loop;
                end link1;


begin
        null;
end test;
------ EOF ------
ls -l chain2n.ada
cat >chain2pkt.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;

        type vector is array(integer range <>) of integer;

        type pkt_type;
        type pkt_ptr is access pkt_type;
        type pkt_type is record
                next:   pkt_ptr;
                header: vector(1..25);
                data:   string(1..50);
                tailer: vector(1..25);
        end record;

        task head is
                entry give(p:in pkt_type);
        end head;

        task link1 is
                entry give(p:in pkt_type);
        end link1;

        task body head is
                pkt : pkt_type;
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 10000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link1.give(pkt);
                                accept give(p:in pkt_type) do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link1 is
                pkt: pkt_type;
                begin
                        loop
                                accept give(p:in pkt_type) do 
                                                null;
                                end give;
                                if printon then put_line("link1"); end if;
                                head.give(pkt);
                        end loop;
                end link1;


begin
        null;
end test;
------ EOF ------
ls -l chain2pkt.ada
cat >chain2ptr.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;

        type vector is array(integer range <>) of integer;

        type pkt_type;
        type pkt_ptr is access pkt_type;
        type pkt_type is record
                next:   pkt_ptr;
                header: vector(1..25);
                data:   string(1..50);
                tailer: vector(1..25);
        end record;

        task head is
                entry give(p:in pkt_ptr);
        end head;

        task link1 is
                entry give(p:in pkt_ptr);
        end link1;

        task body head is
                pkt : pkt_ptr;
                begin
                        pkt := new pkt_type;
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 10000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link1.give(pkt);
                                accept give(p:in pkt_ptr) do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link1 is
                pkt: pkt_ptr;
                begin
                        pkt := new pkt_type;
                        loop
                                accept give(p:in pkt_ptr) do 
                                                null;
                                end give;
                                if printon then put_line("link1"); end if;
                                head.give(pkt);
                        end loop;
                end link1;


begin
        null;
end test;
------ EOF ------
ls -l chain2ptr.ada
cat >chain5.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;

        task head is
                entry give;
        end head;

        task link2 is
                entry give;
        end link2;

        task link3 is
                entry give;
        end link3;

        task link4 is
                entry give;
        end link4;

        task link5 is
                entry give;
        end link5;

        task body head is
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 1000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link2.give;
                                accept give do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link2 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link2"); end if;
                                link3.give;
                        end loop;
                end link2;

        task body link3 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link3"); end if;
                                link4.give;
                        end loop;
                end link3;

        task body link4 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link4"); end if;
                                link5.give;
                        end loop;
                end link4;

        task body link5 is
                begin
                        loop
                                accept give do 
                                                null;
                                end give;
                                if printon then put_line("link5"); end if;
                                head.give;
                        end loop;
                end link5;


begin
        null;
end test;
------ EOF ------
ls -l chain5.ada
cat >guard2.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;
        g1: boolean := true;
        g2: boolean := false;

        task head is
                entry give;
        end head;

        task link1 is
                entry give;
                entry s2;
        end link1;

        task body head is
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 1000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link1.give;
                                accept give do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link1 is
                begin
                        loop
                            select
                                when g1  => accept give do null; end give; 
                             or when g2  => accept s2 do null; end s2;
                            end select;
                                if printon then put_line("link1"); end if;
                                head.give;
                        end loop;
                end link1;


begin
        null;
end test;
------ EOF ------
ls -l guard2.ada
cat >guard20.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;
        g1: boolean := true;
        g2: boolean := false;
        g3: boolean := false;
        g4: boolean := false;
        g5: boolean := false;
        g6: boolean := false;
        g7: boolean := false;
        g8: boolean := false;
        g9: boolean := false;
        g10: boolean := false;
        g11: boolean := false;
        g12: boolean := false;
        g13: boolean := false;
        g14: boolean := false;
        g15: boolean := false;
        g16: boolean := false;
        g17: boolean := false;
        g18: boolean := false;
        g19: boolean := false;
        g20: boolean := false;

        task head is
                entry give;
        end head;

        task link1 is
                entry give;
                entry s2;
                entry s3;
                entry s4;
                entry s5;
                entry s6;
                entry s7;
                entry s8;
                entry s9;
                entry s10;
                entry s11;
                entry s12;
                entry s13;
                entry s14;
                entry s15;
                entry s16;
                entry s17;
                entry s18;
                entry s19;
                entry s20;
        end link1;

        task body head is
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 1000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link1.give;
                                accept give do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link1 is
                begin
                        loop
                            select
                                when g1  => accept give do null; end give; 
                             or when g2  => accept s2 do null; end s2;
                             or when g3  => accept s3 do null; end s3;
                             or when g4  => accept s4 do null; end s4;
                             or when g5  => accept s5 do null; end s5;
                             or when g6  => accept s6 do null; end s6;
                             or when g7  => accept s7 do null; end s7;
                             or when g8  => accept s8 do null; end s8;
                             or when g9  => accept s9 do null; end s9;
                             or when g10  => accept s10 do null; end s10;
                             or when g11  => accept s11 do null; end s11;
                             or when g12  => accept s12 do null; end s12;
                             or when g13  => accept s13 do null; end s13;
                             or when g14  => accept s14 do null; end s14;
                             or when g15  => accept s15 do null; end s15;
                             or when g16  => accept s16 do null; end s16;
                             or when g17  => accept s17 do null; end s17;
                             or when g18  => accept s18 do null; end s18;
                             or when g19  => accept s19 do null; end s19;
                             or when g20  => accept s20 do null; end s20;
                            end select;
                                if printon then put_line("link1"); end if;
                                head.give;
                        end loop;
                end link1;


begin
        null;
end test;
------ EOF ------
ls -l guard20.ada
cat >guard20e.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;
        g1: boolean := true;
        g2: boolean := false;
        g3: boolean := false;
        g4: boolean := false;
        g5: boolean := false;
        g6: boolean := false;
        g7: boolean := false;
        g8: boolean := false;
        g9: boolean := false;
        g10: boolean := false;
        g11: boolean := false;
        g12: boolean := false;
        g13: boolean := false;
        g14: boolean := false;
        g15: boolean := false;
        g16: boolean := false;
        g17: boolean := false;
        g18: boolean := false;
        g19: boolean := false;
        g20: boolean := false;

        task head is
                entry give;
        end head;

        task link1 is
                entry give;
                entry s2;
                entry s3;
                entry s4;
                entry s5;
                entry s6;
                entry s7;
                entry s8;
                entry s9;
                entry s10;
                entry s11;
                entry s12;
                entry s13;
                entry s14;
                entry s15;
                entry s16;
                entry s17;
                entry s18;
                entry s19;
                entry s20;
        end link1;

        task body head is
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 1000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link1.give;
                                accept give do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link1 is
                begin
                        loop
                            select
                                when g2  => accept s2 do null; end s2;
                             or when g3  => accept s3 do null; end s3;
                             or when g4  => accept s4 do null; end s4;
                             or when g5  => accept s5 do null; end s5;
                             or when g6  => accept s6 do null; end s6;
                             or when g7  => accept s7 do null; end s7;
                             or when g8  => accept s8 do null; end s8;
                             or when g9  => accept s9 do null; end s9;
                             or when g10  => accept s10 do null; end s10;
                             or when g11  => accept s11 do null; end s11;
                             or when g12  => accept s12 do null; end s12;
                             or when g13  => accept s13 do null; end s13;
                             or when g14  => accept s14 do null; end s14;
                             or when g15  => accept s15 do null; end s15;
                             or when g16  => accept s16 do null; end s16;
                             or when g17  => accept s17 do null; end s17;
                             or when g18  => accept s18 do null; end s18;
                             or when g19  => accept s19 do null; end s19;
                             or when g20  => accept s20 do null; end s20;
                             or when g1  => accept give do null; end give; 
                            end select;
                                if printon then put_line("link1"); end if;
                                head.give;
                        end loop;
                end link1;


begin
        null;
end test;
------ EOF ------
ls -l guard20e.ada
cat >guard20et.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;
        g1: boolean := true;
        g2: boolean := true;
        g3: boolean := true;
        g4: boolean := true;
        g5: boolean := true;
        g6: boolean := true;
        g7: boolean := true;
        g8: boolean := true;
        g9: boolean := true;
        g10: boolean := true;
        g11: boolean := true;
        g12: boolean := true;
        g13: boolean := true;
        g14: boolean := true;
        g15: boolean := true;
        g16: boolean := true;
        g17: boolean := true;
        g18: boolean := true;
        g19: boolean := true;
        g20: boolean := true;

        task head is
                entry give;
        end head;

        task link1 is
                entry give;
                entry s2;
                entry s3;
                entry s4;
                entry s5;
                entry s6;
                entry s7;
                entry s8;
                entry s9;
                entry s10;
                entry s11;
                entry s12;
                entry s13;
                entry s14;
                entry s15;
                entry s16;
                entry s17;
                entry s18;
                entry s19;
                entry s20;
        end link1;

        task body head is
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 1000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link1.give;
                                accept give do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link1 is
                begin
                        loop
                            select
                                when g2  => accept s2 do null; end s2;
                             or when g3  => accept s3 do null; end s3;
                             or when g4  => accept s4 do null; end s4;
                             or when g5  => accept s5 do null; end s5;
                             or when g6  => accept s6 do null; end s6;
                             or when g7  => accept s7 do null; end s7;
                             or when g8  => accept s8 do null; end s8;
                             or when g9  => accept s9 do null; end s9;
                             or when g10  => accept s10 do null; end s10;
                             or when g11  => accept s11 do null; end s11;
                             or when g12  => accept s12 do null; end s12;
                             or when g13  => accept s13 do null; end s13;
                             or when g14  => accept s14 do null; end s14;
                             or when g15  => accept s15 do null; end s15;
                             or when g16  => accept s16 do null; end s16;
                             or when g17  => accept s17 do null; end s17;
                             or when g18  => accept s18 do null; end s18;
                             or when g19  => accept s19 do null; end s19;
                             or when g20  => accept s20 do null; end s20;
                             or when g1  => accept give do null; end give; 
                            end select;
                                if printon then put_line("link1"); end if;
                                head.give;
                        end loop;
                end link1;


begin
        null;
end test;
------ EOF ------
ls -l guard20et.ada
cat >guard20t.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;
        g1: boolean := true;
        g2: boolean := true;
        g3: boolean := true;
        g4: boolean := true;
        g5: boolean := true;
        g6: boolean := true;
        g7: boolean := true;
        g8: boolean := true;
        g9: boolean := true;
        g10: boolean := true;
        g11: boolean := true;
        g12: boolean := true;
        g13: boolean := true;
        g14: boolean := true;
        g15: boolean := true;
        g16: boolean := true;
        g17: boolean := true;
        g18: boolean := true;
        g19: boolean := true;
        g20: boolean := true;

        task head is
                entry give;
        end head;

        task link1 is
                entry give;
                entry s2;
                entry s3;
                entry s4;
                entry s5;
                entry s6;
                entry s7;
                entry s8;
                entry s9;
                entry s10;
                entry s11;
                entry s12;
                entry s13;
                entry s14;
                entry s15;
                entry s16;
                entry s17;
                entry s18;
                entry s19;
                entry s20;
        end link1;

        task body head is
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 1000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link1.give;
                                accept give do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link1 is
                begin
                        loop
                            select
                                when g1  => accept give do null; end give; 
                             or when g2  => accept s2 do null; end s2;
                             or when g3  => accept s3 do null; end s3;
                             or when g4  => accept s4 do null; end s4;
                             or when g5  => accept s5 do null; end s5;
                             or when g6  => accept s6 do null; end s6;
                             or when g7  => accept s7 do null; end s7;
                             or when g8  => accept s8 do null; end s8;
                             or when g9  => accept s9 do null; end s9;
                             or when g10  => accept s10 do null; end s10;
                             or when g11  => accept s11 do null; end s11;
                             or when g12  => accept s12 do null; end s12;
                             or when g13  => accept s13 do null; end s13;
                             or when g14  => accept s14 do null; end s14;
                             or when g15  => accept s15 do null; end s15;
                             or when g16  => accept s16 do null; end s16;
                             or when g17  => accept s17 do null; end s17;
                             or when g18  => accept s18 do null; end s18;
                             or when g19  => accept s19 do null; end s19;
                             or when g20  => accept s20 do null; end s20;
                            end select;
                                if printon then put_line("link1"); end if;
                                head.give;
                        end loop;
                end link1;


begin
        null;
end test;
------ EOF ------
ls -l guard20t.ada
cat >guard2e.ada <<'------ EOF ------'
-- ada tasking tester
-- task head is the controller
-- tasks link are the chain of tasks
-- tasks idle are the standby tasks

with text_io; use text_io;

procedure test is
        cycles: integer;
        printon: boolean;
        answer: character;
        g1: boolean := true;
        g2: boolean := false;

        task head is
                entry give;
        end head;

        task link1 is
                entry give;
                entry s2;
        end link1;

        task body head is
                begin
                        put("do you want printing (y/n)? ");
                        get(answer);
        put("answer is "); put(answer); put_line(" ");
                        if answer='y' then
                                printon := true;
                        else
                                printon := false;
                        end if;
        if printon then put_line("printing on"); else put_line("print off");
        end if;
                        put("how many cycles? ");
-- doesn't work                 get_line(cycles);
                        cycles := 1000;

                        put_line("started");
                        for i in 1..cycles loop
                                if printon then put_line("head"); end if;
                                link1.give;
                                accept give do 
                                        null;
                                end give;
                        end loop;
                        put_line("ended");
                end head;

        task body link1 is
                begin
                        loop
                            select
                                when g2  => accept s2 do null; end s2;
                             or when g1  => accept give do null; end give; 
                            end select;
                                if printon then put_line("link1"); end if;
                                head.give;
                        end loop;
                end link1;


begin
        null;
end test;
------ EOF ------
ls -l guard2e.ada