TEAM-ADA Archives

Team Ada: Ada Programming Language Advocacy


Options: Use Forum View

Use Monospaced Font
Show Text Part by Default
Show All Mail Headers

Message: [<< First] [< Prev] [Next >] [Last >>]
Topic: [<< First] [< Prev] [Next >] [Last >>]
Author: [<< First] [< Prev] [Next >] [Last >>]

Print Reply
Roger Racine <[log in to unmask]>
Reply To:
Roger Racine <[log in to unmask]>
Thu, 12 Aug 1999 15:44:47 -0400
text/plain (161 lines)
At 01:37 PM 8/12/1999 , Tucker Taft wrote:
>Roger Racine wrote:
>> ...
>Note that in Ada 83, there were no guarantees about the number of
>priorities, and that is why we moved the entire priority model
>to the RT Annex, while at the same time imposing additional requirements,
>such as a minimum of 30 priority levels.
>> ... For a very clear
>> example, we used to be able to run multitasking programs in application
>> mode on every platform (Ada83).  Now, for GNAT using Linux threads (not
>> validated for Annex D), one must be in supervisor mode.
>I don't think these two things are directly related.

Here is how they are related.  For most implementations of Ada83, tasking
was implemented by the runtime system, using one OS process.  It did not
matter what the OS did with priorities (such as changing them to guarantee
service).  With Ada95, it appears that implementers have said: "Well, if
the user wants Annex D, they must want real-time performance.  That is the
whole point of the annex.  If they want real-time performance, they must be
in supervisor mode.  So if they are not in supervisor mode, anything can
happen, since we will not validate Annex D in user mode."  For example, on
Linux if you are in user mode, you are not allowed to change your priority,
except downward.  So an Ada runtime implemented using Linux Threads must be
in supervisor mode to even get priorities set up correctly.

>You may need to be in supervisor mode to disable time-slicing and get
>true "real-time" behavior.  However, you should certainly be able
>to "run multitasking programs" on all Ada 95 platforms without being
>in supervisor mode.  It is possible that you won't get the same guranatees
>about priority-based preemption, but that is presumably because those
>guarantees can't be provided without being in supervisor mode.
>I presume that if you run in non-supervisor mode, the Ada run-time
>does the best it can.  If not, that sounds like an implementation bug,
>not a language bug.

When not in supervisor mode, the implementation supplies the core language
behavior (i.e. no priorities).  Not a bug.  In Ada83, the core language
included priorities.  This behavior would have been a bug.  OK, an
implementor could say they were only providing one priority, but I never
saw that implementation.

You say that "guarantees can't be provided", which needs expansion.  They
can be provided, just not if one implements tasking using services of the
OS that require supervisor mode.  For example, many Ada83 vendors had their
own implementation of tasking.  This was because A) OSs did not support
multiple tasks in the early 80s; or B) those OSs that did support multiple
tasks did not support Ada semantics well enough.

>> ...
>> 2) In Ada83, tasks were very much defined to be concurrent processing
>> paths.  Problems occurred when calling system services (the whole program
>> blocked), but one got used to that and worked around it.  If a program
>> depended on different than concurrent behavior, it was erroneous.  It made
>> for quite portable applications and generally safe designs (i.e. if the
>> priority of a task changed, it did not affect data consistency).  We
>> typically made sure we tested our programs with time-slicing on to make
>> sure it was designed well.  Now it appears that Ada95 requires
>> run-till-blocked semantics, which will lead to designs depending on that
>> semantics.
>Ada 95 certainly doesn't require run-till-blocked semantics.  That
>is only required in the presence of the Task_Dispatching_Policy pragma
>specifying FIFO_Within_Priorities.  In the absence of that pragma,
>any scheduling of same-priority tasks is permitted.

How many vendors provide more than one policy?  I admit I have limited
experience with Ada95 compilers (GNAT), but it would be a pain to support
more than one, and the vendors haven't changed that much from Ada83, and
they have never liked pain. :-)

>Also, the only
>"dependence" on run-till-blocked semantics that I imagined was a
>schedulability dependence.  It is not surprising that schedulability
>would depend on the details of context switching.  I haven't
>heard of using run-till-blocked as a way to get "cheap" mutual
>exclusion, for example.  On the other hand, the *implementation*
>of protected types might be able to take advantage of run-till-blocked
>to reduce the overhead of getting a lock, by simply raising and
>lowering priorities while a lock is held.
>> ...  And second, Ada vendors insisted on taking
>> priorities out of the core language so that it would be easier to implement
>> the core on top of existing threads-based operating systems.  ...
>That was not the motivation.  We wanted to create additional portability
>relating to priorities, by specifying a requirement for more priorities
>and for clearer definition of the priority model.  However, we did not
>feel it was appropriate to impose that on all environments, given that
>Ada 83 allowed implementations to have a single priority.

You fixed something that was not broken.  I would love to see a list of
vendors, host and target pairs, and the number of priorities implemented
for each, for Ada83.  Yes, the number varied, but the vast majority had
more than one.  My guess is that the vast majority had at least 8, and a
majority had at least 30.  To make the minority move up to 30 would have
been a minor change (compared with all the other changes needed for Ada95).

>In any case, based on what you say, it appears that there were some
>*unintended* consequences which effectively made multitasking programs with
>priorities *less* portable.  If true, I certainly agree that is
>undesirable, and we should do something to fix it.  This is the
>first time I have heard this complaint about actual loss of portability.
>Earlier complaints had been about *potential* loss of portability,
>but those seemed unfounded since essentially all vendors made it
>clear they were going to support the real-time annex anyway, and Ada 83
>made no guarantees about supporting multiple priorities.

The language did not, but virtually all the vendors did implement some
number of priorities.  For our purposes, there were enough.

>There is an "ARG" meeting coming up in September, and perhaps we can
>try to address this apparent loss of portability.  If you could give
>a very specific example that would help.  For example, what happens
>when you run your program without being in supervisor mode?  Does it
>behave strangely?  What actually are the "fall-back" semantics when
>run in non-supervisor mode?  etc...

As I mentioned above, in user mode the core language is implemented.
Pragmas defined in Annex D are essentially ignored.  No warning message.
Just "strange" behavior (all tasks have equal priorities).  As far as I can
tell, this is permitted by the language.

I do not have a lot of trust in the "ARG" (has it changed its name?),
especially on this.  If what I have been told about implementations is
true, vendors will be very resistant to change, and it would likely require
a major change.  Besides, some application developers will argue that they
have gotten used to using system services in tasks.  Others will argue that
Ada tasking with priorities is no different than using threads in C.
Unfortunately, these are all valid arguments now.

My specific application is a non-real-time simulation of a real-time
application.  I model the real-time clock by using a low-priority task that
has a loop.  Each time through the loop this task rendezvous with a
high-priority task that increments the clock.  All other tasks use a
service to run cyclically, essentially like the "delay until" mechanism.
On the real target, this performs in real time.  On the host, the task is
put on a queue to be woken when the simulated clock reaches the correct
time.  Each time the clock ticks, the queue is checked, and all tasks that
should be released are released.

The effect of this, if priorities work correctly (on a uniprocessor), is
that the clock will never tick unless all the application tasks are
blocked, either waiting for something (like an event to occur, implemented
as a protected record or rendezvous), or waiting on the timer queue.

This has been extremely useful, for us, for initial development of
real-time embedded systems.
Roger Racine
Draper Laboratory, MS 31
555 Technology Sq.
Cambridge, MA 02139, USA