comp.lang.ada
 help / color / mirror / Atom feed
* Re: Q: Protected types and entries (long)
  1999-02-16  0:00 Q: Protected types and entries (long) Erik Margraf
@ 1999-02-16  0:00 ` Wilhelm Spickermann
  1999-02-19  0:00 ` Samuel Mize
  1999-03-01  0:00 ` Robert A Duff
  2 siblings, 0 replies; 4+ messages in thread
From: Wilhelm Spickermann @ 1999-02-16  0:00 UTC (permalink / raw)


Erik Margraf wrote:
> 
> Recently I started to learn about protected types. I tried to do
> something like the following:
> 
> (Not 100% Ada :-)
> 
> ...
>    type buffer is array (...) of character;
>    protected type Object is
>      entry put (data : in buffer);
>    private
>      internal_buffer : array (BIG_Enough) of character;
>      items_in_buffer : integer := 0;
>    end;
> ...
>    protected Object body
> 
>    entry put (data : in buffer)
>         when data'size + items_in_buffer <= internal_buffer�size is
>    begin
>     ...
>    end;
>    end Object;
> 
> Since the reference to a formal parameter of an entry is not allowed,
> gnat
> refused to compile this ;-). I changed the code to
> 
>    type buffer is array (...) of character;
>    protected type Object is
>      entry put (data : in buffer);
>    private
>      entry p_put (data : in buffer);
>      internal_buffer : array (BIG_Enough) of character;
>      data_size : integer;
>      items_in_buffer : integer := 0;
>    end;
> ...
>    protected Object body
>    entry put (data : in buffer)
>         when true is
>    begin
>       data_size := data�size;
>       if data_size + items_in_buffer > internal_buffer�size then
>         requeue p_put;
>       end if;
>    end;
> 
>    entry p_put (data : in buffer)
>         when data_size + items_in_buffer <= internal_buffer�size is
>    begin
>     ...
>    end;
>    end Object;
> 
> This compiles.
> Now my questions:
> 
>         - Can someone tell me WHY this limitation in the barrier exists?
>         - Is my "solution" really a solution to this problem?
>         - (What) Should I do something different?
> Thanks
> 
> Erik Margraf
> 
> --
> --------------------------------------------------------------------
> -- Erik Margraf
> -- Siemens Austria PSE KB2
> -- erik.margraf@siemens.at
> -- +43 1 1707 45887
> --------------------------------------------------------------------
I think the second solution will not work. Think of several tasks
waiting
for the barrier of p_put to become open. Then one task with a small
data'size enters put: It will change data_size and open the barrier for
all of them...
(The barrier does not belong to a call -- it belongs to the entry!)

The solution to your problem is decribed in Burns, Wellings: Concurrency 
in Ada (8.1.2).

The reason for the limitations on barriers is the high efficiency of 
implementation which was achieved by it. Protected objects are even more
efficient than semaphores, because one thread of control can execute the
protected operation of another and continue to work without any context
switch. 

Example: We have a one element buffer which is initially empty, a task R
         which is reading elements all day long and task W which is
writing
         all day:
 - task R tries to read and gets blocked
 - task W writes
 - task W executes the protected read of R and makes R executable
 - task W writes
 - task W tries to write the third element and gets blocked
 (cf. Ada 95 Rationale: 9.1.3)

Wilhelm Spickermann




^ permalink raw reply	[flat|nested] 4+ messages in thread

* Q: Protected types and entries (long)
@ 1999-02-16  0:00 Erik Margraf
  1999-02-16  0:00 ` Wilhelm Spickermann
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Erik Margraf @ 1999-02-16  0:00 UTC (permalink / raw)


Recently I started to learn about protected types. I tried to do 
something like the following:

(Not 100% Ada :-)

...
   type buffer is array (...) of character;
   protected type Object is
     entry put (data : in buffer);
   private
     internal_buffer : array (BIG_Enough) of character;
     items_in_buffer : integer := 0;
   end; 
...
   protected Object body

   entry put (data : in buffer) 
	when data'size + items_in_buffer <= internal_buffer´size is
   begin
    ...
   end;
   end Object;

Since the reference to a formal parameter of an entry is not allowed,
gnat
refused to compile this ;-). I changed the code to

   type buffer is array (...) of character;
   protected type Object is
     entry put (data : in buffer);
   private
     entry p_put (data : in buffer);
     internal_buffer : array (BIG_Enough) of character;
     data_size : integer;
     items_in_buffer : integer := 0;
   end; 
...
   protected Object body
   entry put (data : in buffer) 
	when true is
   begin
      data_size := data´size;
      if data_size + items_in_buffer > internal_buffer´size then 
        requeue p_put;
      end if;
   end;

   entry p_put (data : in buffer) 
	when data_size + items_in_buffer <= internal_buffer´size is
   begin
    ...
   end;
   end Object;

This compiles. 
Now my questions:

	- Can someone tell me WHY this limitation in the barrier exists?
	- Is my "solution" really a solution to this problem?
	- (What) Should I do something different?
Thanks

Erik Margraf

-- 
--------------------------------------------------------------------
-- Erik Margraf
-- Siemens Austria PSE KB2
-- erik.margraf@siemens.at
-- +43 1 1707 45887
--------------------------------------------------------------------




^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Q: Protected types and entries (long)
  1999-02-16  0:00 Q: Protected types and entries (long) Erik Margraf
  1999-02-16  0:00 ` Wilhelm Spickermann
@ 1999-02-19  0:00 ` Samuel Mize
  1999-03-01  0:00 ` Robert A Duff
  2 siblings, 0 replies; 4+ messages in thread
From: Samuel Mize @ 1999-02-19  0:00 UTC (permalink / raw)


Erik Margraf <erik.margraf@siemens.at> wrote:
>    entry put (data : in buffer) 
>    when data'size + items_in_buffer <= internal_buffer'size is
>    begin
...
>    - Can someone tell me WHY this limitation in the barrier exists?

Because the barrier expression applies to the whole entry queue, not
to each call in the queue individually.

Why?

Remember that "a protected object is designed to be a very efficient
conditional critical region[1]."

Now, there are two ways that the reference to "data" could be
interpreted.  It could mean to look at the call at the head of the
queue, or it it could mean to review each call individually and
see if it can execute.

If it looks only at the call at the head of the queue, one task trying
to add a large item might block any number of tasks trying to add
items that would fit, depending on whether or not it got there first.
This kind of race condition makes the system's behavior much less
predictable, which is a bad thing in hard-deadline real-time systems.

If it looks at all entries in the queue, then the amount of time that
it takes to evaluate the barriers can grow without limit, also a bad
thing in a hard real-time environment.

So the most efficient construct is the one chosen for Ada.  However,
sometimes one does need to have each queue element examined in turn,
and that's where Ada's "requeue" come into play, as you thought.

However, your solution has two flaws.  First, if the object fits
into the buffer, it won't be added -- you don't do anything in the
body of "put".  That's a nit, and you can fix it by always requeueing
in put.[2]

Second, and much more important, consider the case where you have
two entries queued up on "put", the first with a larger "data" than
the second has, and both larger than your current free space.  The
first will queue up on "p_put", then the second will queue up on
"p_put", and data_size will show the second (smaller) size request.
Then a consumer task frees up just that much space, and the first
call is free to execute without enough space in the internal buffer.

So, how do you solve your problem?

1. If you want the entries to be processed in order of arrival, you
   can prevent a second "put" call from resetting "data_size" by
   guarding "put" with an initially-true boolean -- call it
   "data_size_unset".  Then, in the body of "put", you set data_size
   and set "data_size_unset" to false, and requeue on "p_put".
   "p_put" would set "data_size_unset" back to true.

2. If you want the entries to be scanned, and any that are small
   enough processed, do this.

   Have "put" add the item to the internal buffer if there is room.
   Otherwise it sets a boolean to false, and requeues the call on an
   internal entry, whose barrier is that boolean.

   When your consumer task frees up some space, it sets that boolean
   to be true.  The internal entry just requeues on "put".  Thus,
   each time some space is freed up, everybody tries "put" again and
   either succeeds or get requeued once more.

You can fancy up that second approach.  For instance, "p_put" might save
the largest data size that would fit, and then requeue everybody on
"p_p_put", which would requeue one request of that size to "put" and
everybody else back to "p_put" (or something like that, you need to
carefully think through the queue behavior in a design like this, and
I haven't done so for this example.)

The point is, you can create arbitrarily complex scanning behavior with
inter-acting queues.  And you aren't paying task switching overhead to
do it, because of the way that protected objects operate.  But you *are*
creating a lot more overhead than they wanted to put into the base
design of Ada, so you have to code it manually.

Best,
Sam Mize

[1] Ada 95 Rationale, 9.1

[2] Or, you can duplicate the code from "p_put" in the body of "put",
which would give precedence to callers with smaller "data" buffers.
If you do this, you should encapsulate the shared code in a procedure,
and call that procedure in both entries.


-- 
Samuel Mize -- smize@imagin.net (home email) -- Team Ada
Fight Spam: see http://www.cauce.org/ \\\ Smert Spamonam




^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Q: Protected types and entries (long)
  1999-02-16  0:00 Q: Protected types and entries (long) Erik Margraf
  1999-02-16  0:00 ` Wilhelm Spickermann
  1999-02-19  0:00 ` Samuel Mize
@ 1999-03-01  0:00 ` Robert A Duff
  2 siblings, 0 replies; 4+ messages in thread
From: Robert A Duff @ 1999-03-01  0:00 UTC (permalink / raw)


Erik Margraf <erik.margraf@siemens.at> writes:

> ...Since the reference to a formal parameter of an entry is not
> allowed [in a barrier],...

When you wish you could do that, it often makes sense to use an entry
family.  The family index behaves somewhat like a parameter, but you
*can* use it in the barrier.  Another alternative is requeue, which you
already knew.

- Bob
-- 
Change robert to bob to get my real email address.  Sorry.




^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~1999-03-01  0:00 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-02-16  0:00 Q: Protected types and entries (long) Erik Margraf
1999-02-16  0:00 ` Wilhelm Spickermann
1999-02-19  0:00 ` Samuel Mize
1999-03-01  0:00 ` Robert A Duff

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox