Re: Advantages
From: Nick Roberts (nick.roberts_at_acm.org)
Date: 06/27/04
- Next message: Pylinius: "Re: Improving Ada's Image"
- Previous message: Jacob Sparre Andersen: "Re: [newbie] simple(?) data structures"
- In reply to: Andrew Carroll: "Advantages"
- Next in thread: Pascal Obry: "Re: Advantages"
- Reply: Pascal Obry: "Re: Advantages"
- Reply: Robert I. Eachus: "Re: Advantages"
- Reply: Marin David Condic: "Re: Advantages"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Sun, 27 Jun 2004 16:16:59 +0100
"Andrew Carroll" <andrew@carroll-tech.net> wrote in message
news:mailman.165.1088318818.391.comp.lang.ada@ada-france.org...
> ... What features of Ada make it easier to detect pitfalls
> in parallel programming? What features of Ada help with
> debugging?
I don't wish to cross with Marin in answering this, but forgive me if I
throw my oar in a bit.
Suppose I have two threads (another name for 'tasks') which both need to
read a variable (which is a struct (record)) from time to time, and one of
them also updates the variable from time to time. When an update is done, it
involves assigning values to several of the variable's members (components),
so the update is not 'atomic'. This means that there needs to be some kind
of synchronisation between the threads, to prevent one thread trying to read
the variable right in the middle of it being updated.
In most programming languages, including C for example, under a typical
execution environment or OS, the usual or only way to do this will be by the
explicit use of semaphores (or a similar facility). When a thread wants to
read or update the variable, it sets the semaphore (which may cause it to
wait (to be 'blocked')). When it has finished, it resets the semaphore
(which may unblock another waiting thread). Let's say the function to set a
semaphore is named 'sem_p', and the function to reset it is 'sem_v'. If the
variable is named 'r' (and has members 'x' and 'y') and the semaphore is
named 's', a C program might contain code like this:
/* Thread A */
...
sem_p(s); /* set the semaphore, maybe wait */
x = r.x; y = r.y; /* read the variable */
sem_v(s); /* reset the semaphore */
...
/* Thread B */
...
sem_p(s); /* set the semaphore, maybe wait */
r.x = x; r.y = y; /* update the variable */
sem_v(s); /* reset the semaphore */
...
This is all well and dandy, but there are many things that can go wrong. For
example, supposing a programmer accidentally omitted one of the calls to
sem_p or sem_v? Or accidentally used the wrong semaphore in one of the
calls? This kind of mistake is easily done in a big, complex program with
lots of threads and semaphores. The consequences are often hard to debug: it
is typical that a problem is only manifested intermittently; it is typical
that using a debugger (single stepping or breakpoint jumping) destroys the
temporal circumstances of normal execution that are needed to observe the
problem at all; it is typical that, even when the problem is observed, the
cause is subtle and very hard to determine.
In Ada 95, you would typically use a protected object to protect the
variable from unwanted parallel access. (In Ada 83 you would typically have
used a task and a 'rendezvous', but I'll illustrate the protected object
approach here.)
type R_Type is
record
X, Y: Float;
...
end record;
protected Prot_R is
function Get return R_Type;
procedure Set (New_R: in R_Type);
private
Inner_R: R_Type;
end;
protected body Prot_R is
function Get return R_Type is
begin
return Inner_R;
end;
procedure Set (New_R: in R_Type) is
begin
Inner_R := New_R;
end;
end Prot_R;
...
--- in task A
declare
Local_R: R_Type;
begin
Local_R := Prot_R.Get; -- read variable
X := Local_R.X;
Y := Local_R.Y;
...
--- in task B
Prot_R.Set((X,Y,...)); -- update variable
Because the use of OS primitives such as semaphores is hidden from the user
by Ada, many of the mistakes that would be easy to make in the C code are
either impossible -- for example, we cannot omit a call to set or reset a
semaphore, since Ada does these for us invisibly -- or caught by the
compiler and reported to us in an obvious way -- for example, if we tried to
use 'Inner_R' in an expression in task A or B, the compiler would complain
that Inner_R is not visible at that point (because it is private to Prot_R).
Obviously this example is simplistic compared to real software, but I hope
it gives a flavour of Ada's advantages in concurrent programming.
You might like to note that the use of an Ada protected object gives further
advantages than I have mentioned so far. It is implemented in such a way
that functions are permitted to be parallel with one another (but not with
any procedure). So, tasks A and B would be permitted to read Prot_R
simultaneously, but no task would be allowed to read it while another was in
the middle of setting it. Protected objects can provide yet more
sophisticated functionality (with 'entries'). And then there are tasks,
rendezvouses, and various other facilities too. All of this functionality
can be achieved in other languages, but generally only at a lower and more
error-prone level.
Really, one could write a big book about concurrent programming in Ada.
Hehe, in fact I'm sure somebody has! I'm sorry, I can't recall the details,
but I'm sure someone else will oblige.
-- Nick Roberts
- Next message: Pylinius: "Re: Improving Ada's Image"
- Previous message: Jacob Sparre Andersen: "Re: [newbie] simple(?) data structures"
- In reply to: Andrew Carroll: "Advantages"
- Next in thread: Pascal Obry: "Re: Advantages"
- Reply: Pascal Obry: "Re: Advantages"
- Reply: Robert I. Eachus: "Re: Advantages"
- Reply: Marin David Condic: "Re: Advantages"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|
|