Ada - MIT

Ada
Constructs
Revisited
21 Oct. 2002
16-035
1
Constructs to be Expanded
•
Generics
•
Tasking
•
Elaboration
16-035
2
Generics
•
Generic units are used to make Ada’s strong types less painful
— Possible to create general unit once, but use it for many
different declared types
— Has types, variables, subprograms, and packages as parameters
— When each instance is made of a generic unit, it might be a
separate copy, or it might share code
16-035
3
Non-Generic Version
package Complex_Numbers is
type Complex is private;
...
function Construct (Real : Float; Imaginary : Float) return Complex;
function “+” (X, Y: Complex) return Complex;
...
private
type Complex is record
Real_Part : Float;
Imaginary_Part : Float;
end record;
end;
16-035
4
Non-Generic Body
package body Complex_Numbers is
...
function Construct (Real : Float; Imaginary : Float) return Complex is
begin
return Complex’(Real, Imaginary);
end Construct;
function “+” (X, Y: Complex) return Complex is
begin
return Complex’( (X.Real_Part + Y.Real_Part),
(X.Imaginary_Part + Y.Imaginary_Part) );
end “+”;
...
end Complex_Numbers;
16-035
5
Generic Example
generic
type My_Float is digits <>;
package Generic_Complex_Numbers is
type Complex is private;
...
function Construct (Real : My_Float; Imaginary : My_Float) return Complex;
function “+” (X, Y: Complex) return Complex;
...
private
type Complex is record
Real_Part : My_Float;
Imaginary_Part : My_Float;
end record;
end;
16-035
6
Generic Body – Almost Identical to Non-Generic
package body Generic_Complex_Numbers is
...
function Construct (Real : My_Float; Imaginary : My_Float) return Complex is
begin
return Complex’(Real, Imaginary);
end Construct;
function “+” (X, Y: Complex) return Complex is
begin
return Complex’( (X.Real_Part + Y.Real_Part),
(X.Imaginary_Part + Y.Imaginary_Part) );
end “+”;
...
end Generic_Complex_Numbers;
16-035
7
Instantiating a Generic
with Generic_Complex_Numbers;
procedure Test
is
type Small_Float_type is digits 6;
type Large_Float_type is digits 14;
package Small_Complex is new Generic_Complex_Numbers (Small_Float_type);
use Small_Complex;
package Large_Complex is new Generic_Complex_Numbers (Large_Float_type);
use Large_Complex;
Small_Real_Part : Small_Float_type := 1.234_567;
Small_Imaginary_Part : Small_Float_type := 2.345_678;
Large_Real_Part : Large_Float_type := 3.456_789_101_112;
Large_Imaginary_Part : Large_Float_type := 4.567_891_011_121_3;
Small_Complex_Number : Small_Complex.Complex := Construct (Small_Real_Part,
Small_Imaginary_Part);
Large_complex_Number : Large_Complex.Complex := Construct (Large_Real_part,
Large_Imaginary_Part);
begin
Small_Complex_Number := Small_Complex_Number + Small_Complex_Number;
Large_Complex_Number := Large_Complex_Number + Large_Complex_Number;
end Test;
16-035
8
Tasking
•
Multitasking (what UNIX people call Threads) is built in to the Ada
language
— Model is parallel activities
— Easily implemented to use multiple processors
— Implementation can use any scheduling mechanism
» Time slicing, where each task (of the same priority) gets a bit
of time, then the next task runs
» One task runs until it hits a blocking point, then the next task
(of the same priority or lower) runs
— Priority can be used (if supported) to force the system to run
tasks in a certain order (if running on a single processor system)
16-035
9
The rendezvous
•
•
“two people meet, perform a transaction and then go on
independently”
Between two tasks: one task calls an entry declared in another.
Task Specification
task T is
entry E( … );
end;
Some other Task
T.E( … );
Task Body
accept E( … ) do
--sequence of statements
end E;
16-035
10
Example: semaphore
Simple mechanism to create critical sections: section of code that must be executed
by only one task at a time
task type Semaphore is
entry P;
entry V;
end Semaphore;
-- Dijkstra’s terminology
-- Passeren (lock)
-- Vrimajken (unlock)
task body Semaphore is
begin
loop
accept P;
accept V;
-will not accept another P until a caller asks for V
end loop;
end Semaphore;
16-035
11
Using a semaphore
•
A task that needs exclusive access to the critical section executes:
Semaphore.P;
…
Semaphore.V;
•
•
--
critical section code
If in the meantime another task calls Semaphore.P, it blocks, because the semaphore does not
accept a call to P until after the next call to V: the other task is blocked until the current one
releases by making an entry call to V.
Programming hazards:
–someone else may call V : race condition
–no one calls V: other callers are deadlocked
–Starvation is possible if the queue is not a FIFO queue
16-035
12
Timing and scheduling
•
•
•
•
Time slicing
Priorities
Real-Time Systems annex (Annex D)
Task can be held up of different reasons
— Waiting for:
» a partner in a rendezvous
» dependent task to terminate
— delay 3.0;
Seconds: constant duration := 1.0;
Minutes: constant Duration := 60.0;
Hours: constant Duration := 3600.0;
delay
2*Hours+40*Minutes;
16-035
13
Example
loop
delay 5*Minutes;
Action;
end loop;
Want cyclic execution.
Any problems with this solution?
16-035
14
Same, but different …
declare
use Calendar;
Interval: constant Duration := 5*Minutes;
Next_Time: Time := First_Time;
begin
loop
delay until Next_Time;
Action;
Next_Time := Next_Time + Interval;
end loop;
end;
16-035
15
“Protected Variable”
•
Protect a variable V from uncontrolled access.
package Protected_Variable is
procedure Read(X: out Item);
procedure Write(X: in Item);
end;
package body Protected_Variable is
V: Item := initial value;
Any problems?
procedure Read(X: out Item) is
begin
X := V;
end;
type Item is
record
X_Coord: Float;
Y_Coord: Float;
end record;
procedure Write(X: in Item) is
begin
V := X;
end;
end Protected_Variable;
16-035
16
Protected Object Example
package Variable is
type My_Type is . . .;
protected P_Variable is
function Read return My_Type; -- Many callers
procedure Write (
-- One caller
X : in
My_Type );
private
V : My_Type := 0;
end P_Variable;
end Variable;
•
•
•
Any number of tasks may call protected functions at the same time
Protected functions can not write to protected objects
Not possible for a task to call Write while another calls Read (the
second will block)
16-035
17
Bounded buffer
I
J
N: constant := 8;
type Index is mod N;
type Item_Array is array (Index) of Item;
protected type Buffering is
entry Put(X: in Item);
entry Get(X: out Item);
private
A: Item_Array;
I,J: Index := 0;
Count: Integer range 0..N := 0;
end Buffering;
My_Buffer : Buffering;
…
My_Buffer.Put(X);
16-035
protected body Buffering is
entry Put(X: in Item) when Count < N is
begin
A(I) := X;
I := I+1;
Count := Count + 1;
end Put;
entry Get(X: out Item) when Count >0 is
begin
X := A(J);
J := J + 1;
Count := Count – 1;
end Get;
end Buffering;
18
Elaboration
•
•
Ada programs perform work before the first executable statement
after the “begin” of the main procedure is run
— “Elaboration” code is executed first
» Gives data initial values (which might call functions)
» Creates objects (if dynamic)
» Runs package initialization code
To be completely precise, the whole program is one big elaboration
— Package specifications are elaborated before their bodies
— Package bodies are elaborated before any of their subprograms
can be called
— Package bodies can have task bodies, which start running after
the package body has been elaborated
— When all packages have been elaborated, the main procedure
runs
16-035
19
Elaboration Example
with Ada.Text_IO;
procedure Elab_Test is
package Random_Pack is -- Elaboration of this is first
type My_Type is digits 6 range 0.0 .. 1.0;
function Random return My_Type;
end Random_Pack;
package body Random_Pack is -- Elaboration of this is second
Seed : My_Type;
function Random return My_Type is
task My_Task;
task body My_Task is
X : My_Type := Random;
begin
Ada.Text_IO.Put ("Made it");
end My_Task;
begin
return Seed; -- For now, return a constant
end Random;
begin
Seed := 0.5; -- Need this before Random can be called
end Random_Pack; -- After last statement after "begin", My_Task can run
use Random_Pack;
Z : My_Type := Random; -- Executes the Random function
begin
null;
end Elab_Test;
16-035
20
Watch Out for Circular Dependencies
package My_Pack is
type My_type is range 1_000 .. 1_000_000;
procedure Do_Something (Parameter : in out My_type);
end My_Pack;
package Other_Pack is
type Other_type is range 0 .. 1_000;
function Do_Something_Else return Other_type;
end Other_Pack;
with Other_Pack;
pragma Elaborate (Other_Pack);
package body My_Pack is
X : Other_Pack.Other_type := Do_Something_Else;
...
end My_Pack;
with My_Pack;
package body Other_Pack is
X : My_Pack.My_type;
...
end Other_Pack;
16-035
21
-- Needs Other_Pack body elab