comp.lang.ada
 help / color / mirror / Atom feed
* How to avoid unreferenced objects (mutexes etc)
@ 2002-01-11 13:48 Dmitry A. Kazakov
  2002-01-11 13:52 ` Lutz Donnerhacke
                   ` (4 more replies)
  0 siblings, 5 replies; 41+ messages in thread
From: Dmitry A. Kazakov @ 2002-01-11 13:48 UTC (permalink / raw)


Hi all!

I have a question.

Let I implement mutex and locking objects as follows:

protected type Mutex is
   entry Seize;
   procedure Release;
private
   Owned : Boolean := False;
end Mutex;

type Lock (Resource : access Mutex) is new
   Ada.Finalization.Limited_Controlled with null record;
procedure Initialize (Object : in out Lock) is
begin
   Object.Resource.Seize;
end Initialize;
procedure Finalize (Object : in out Lock) is
begin
   Object.Resource.Release;
end Finalize;

The idea is to write critical sections as follows:

   Temp : Lock (Mutex_of_a_resource'Access);
begin
   ...  -- Safe access to the resource
end; -- Mutex is released even if an exception propagates

The problem is that the object Temp is never referenced. The compiler
complains of that, but it is a minor problem. The questions are

1. Has the compiler right to optimize out Temp?
2. Is there a better solution?

Thanks,
Dmitry Kazakov



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-11 13:48 How to avoid unreferenced objects (mutexes etc) Dmitry A. Kazakov
@ 2002-01-11 13:52 ` Lutz Donnerhacke
  2002-01-11 14:47 ` Robert A Duff
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 41+ messages in thread
From: Lutz Donnerhacke @ 2002-01-11 13:52 UTC (permalink / raw)


* Dmitry A. Kazakov wrote:
>The problem is that the object Temp is never referenced.
>The compiler complains of that, but it is a minor problem.

You can use a compiler specific pragma to supress this warning.

>The questions are
> 1. Has the compiler right to optimize out Temp?

No. There are initialisation and finalization actions which has to be taken.
Other example "t : task_access := new task_type;" is also often "unused".

> 2. Is there a better solution?

That's a wonderful canoncical pattern.



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-11 13:48 How to avoid unreferenced objects (mutexes etc) Dmitry A. Kazakov
  2002-01-11 13:52 ` Lutz Donnerhacke
@ 2002-01-11 14:47 ` Robert A Duff
  2002-01-11 18:02 ` Jeffrey Carter
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 41+ messages in thread
From: Robert A Duff @ 2002-01-11 14:47 UTC (permalink / raw)


dmitry@elros.cbb-automation.de (Dmitry A. Kazakov) writes:

>    Temp : Lock (Mutex_of_a_resource'Access);

> The problem is that the object Temp is never referenced. The compiler
> complains of that, but it is a minor problem. The questions are
> 
> 1. Has the compiler right to optimize out Temp?

No.  There are some extra permissions to "optimize" away pseudo-dead
variables of a non-limited type, even when init/fin have side effects.
(there's an AI on that.)  But your type is limited, and therefore immune
to such meddling.

> 2. Is there a better solution?

Your solution is fine.

- Bob



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-11 13:48 How to avoid unreferenced objects (mutexes etc) Dmitry A. Kazakov
  2002-01-11 13:52 ` Lutz Donnerhacke
  2002-01-11 14:47 ` Robert A Duff
@ 2002-01-11 18:02 ` Jeffrey Carter
  2002-01-11 19:40 ` Robert Dewar
  2002-01-12  1:11 ` Nick Roberts
  4 siblings, 0 replies; 41+ messages in thread
From: Jeffrey Carter @ 2002-01-11 18:02 UTC (permalink / raw)


"Dmitry A. Kazakov" wrote:
> 
> The idea is to write critical sections as follows:
> 
>    Temp : Lock (Mutex_of_a_resource'Access);
> begin
>    ...  -- Safe access to the resource
> end; -- Mutex is released even if an exception propagates
> 
> 2. Is there a better solution?

Use a dedicated monitor to implement the critical sections.

-- 
Jeffrey Carter



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-11 13:48 How to avoid unreferenced objects (mutexes etc) Dmitry A. Kazakov
                   ` (2 preceding siblings ...)
  2002-01-11 18:02 ` Jeffrey Carter
@ 2002-01-11 19:40 ` Robert Dewar
  2002-01-12 10:18   ` Martin Dowie
  2002-01-14  8:54   ` Dmitry A. Kazakov
  2002-01-12  1:11 ` Nick Roberts
  4 siblings, 2 replies; 41+ messages in thread
From: Robert Dewar @ 2002-01-11 19:40 UTC (permalink / raw)


dmitry@elros.cbb-automation.de (Dmitry A. Kazakov) wrote in message news:<3c3ee8c8.105408250@News.CIS.DFN.DE>...
> The problem is that the object Temp is never referenced. 
> The compiler
> complains of that, but it is a minor problem. The 
> questions are


In GNAT, we just introduced 

   pragma Unreferenced (entity-name, entity-name, ....);

specifically to deal with entities that are deliberately
unreferenced. This pragma supresses unreferenced warnings
for the entity, and gives an error if the entity is actually referenced.

It provides useful documentation that the entity is really
really intended to be there, and serves a purpose even
though it is not specifically referenced.



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-11 13:48 How to avoid unreferenced objects (mutexes etc) Dmitry A. Kazakov
                   ` (3 preceding siblings ...)
  2002-01-11 19:40 ` Robert Dewar
@ 2002-01-12  1:11 ` Nick Roberts
  2002-01-12 22:04   ` Matthew Heaney
  2002-01-14  9:42   ` Dmitry A. Kazakov
  4 siblings, 2 replies; 41+ messages in thread
From: Nick Roberts @ 2002-01-12  1:11 UTC (permalink / raw)


"Dmitry A. Kazakov" <dmitry@elros.cbb-automation.de> wrote in message
news:3c3ee8c8.105408250@News.CIS.DFN.DE...

> protected type Mutex is
>    entry Seize;
>    procedure Release;
> private
>    Owned : Boolean := False;
> end Mutex;
>
> type Lock (Resource : access Mutex) is new
>    Ada.Finalization.Limited_Controlled with null record;

> procedure Initialize (Object : in out Lock) is
> begin
>    Object.Resource.Seize;
> end Initialize;

> procedure Finalize (Object : in out Lock) is
> begin
>    Object.Resource.Release;
> end Finalize;
>
> The idea is to write critical sections as follows:
>
>    Temp : Lock (Mutex_of_a_resource'Access);
> begin
>    ...  -- Safe access to the resource
> end; -- Mutex is released even if an exception propagates


This may well be simply my own taste, but I would much prefer the critical
section to be written thus:


  type Indirect_Mutex is access all Mutex;

  The_Lock: aliased Mutex;

  ...

    Lock: [constant] Indirect_Mutex := The_Lock'Access;
  begin
    -- here we can do any non-critical pre-processing
    Lock.Seize;
    begin
      -- critical code goes here
    exception
      when others =>
        -- reset resource state to something stable
    end;
    Lock.Release;
    -- here we can do any non-critical post-processing
  end;


This way, no mucking about with finalisation or unreferenced objects is
required, and you have natural places to put pre- and post-processing code.
More importantly, this scheme ensures that if some critical processing
(presumably messing about with the state of the resource) does go wrong, the
resource is 'cleaned up' before any other innocent task can try using it.

However, really, a much better design is like this:


  protected type My_Resource_Type is
    procedure Do_Something_Critical;
  private
    ... -- resource state
  end;

  ...

  protected body My_Resource_Type is

    procedure Do_Something_Critical is
    begin
      -- critical code here
    exception
      when others =>
        -- reset resource state to something stable
    end;

  end My_Resource_Type;

  ...

  The_Resource: My_Resource_Type;

  ...

  begin
    -- pre-processing
    The_Resource.Do_Something_Critical;
    -- post-processing
  end;


The general idea is that a mutex is a low-level synchronisation construct,
and Ada provides a higher-level construct (protected objects) that takes
away much of the pain and danger of using mutexes directly.

--
Best wishes,
Nick Roberts






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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-11 19:40 ` Robert Dewar
@ 2002-01-12 10:18   ` Martin Dowie
  2002-01-14  8:54   ` Dmitry A. Kazakov
  1 sibling, 0 replies; 41+ messages in thread
From: Martin Dowie @ 2002-01-12 10:18 UTC (permalink / raw)


"Robert Dewar" <dewar@gnat.com> wrote in message
news:5ee5b646.0201111140.48bf30e2@posting.google.com...
> dmitry@elros.cbb-automation.de (Dmitry A. Kazakov) wrote in message
news:<3c3ee8c8.105408250@News.CIS.DFN.DE>...
> In GNAT, we just introduced
>
>    pragma Unreferenced (entity-name, entity-name, ....);
>
> specifically to deal with entities that are deliberately
> unreferenced. This pragma supresses unreferenced warnings
> for the entity, and gives an error if the entity is actually referenced.
>
> It provides useful documentation that the entity is really
> really intended to be there, and serves a purpose even
> though it is not specifically referenced.

This sounds unbelievable useful, is it going to be proposed for Ada200X?





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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-12  1:11 ` Nick Roberts
@ 2002-01-12 22:04   ` Matthew Heaney
  2002-01-13  5:45     ` Nick Roberts
  2002-01-14  8:31     ` Jean-Pierre Rosen
  2002-01-14  9:42   ` Dmitry A. Kazakov
  1 sibling, 2 replies; 41+ messages in thread
From: Matthew Heaney @ 2002-01-12 22:04 UTC (permalink / raw)



"Nick Roberts" <nickroberts@adaos.worldonline.co.uk> wrote in message
news:a1o2h1$sfe3f$5@ID-25716.news.dfncis.de...
> However, really, a much better design is like this:

No, because protected operations can't make blocking calls.

Note that all predefined I/O packages are "potentially blocking," which
means, for example, that you can't call Text_IO from inside a protected
object.

So you still need a semaphore-style type (as in the original post), to
handle the situation in which blocking calls are made inside a critical
section.  The technique shown using a Limited_Controlled type to
automatically seize and release the semaphore is a standard Ada95 idiom.









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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-12 22:04   ` Matthew Heaney
@ 2002-01-13  5:45     ` Nick Roberts
  2002-01-13  8:21       ` tmoran
                         ` (3 more replies)
  2002-01-14  8:31     ` Jean-Pierre Rosen
  1 sibling, 4 replies; 41+ messages in thread
From: Nick Roberts @ 2002-01-13  5:45 UTC (permalink / raw)


"Matthew Heaney" <mheaney@on2.com> wrote in message
news:u41cg9jaqnrs56@corp.supernews.com...

> "Nick Roberts" <nickroberts@adaos.worldonline.co.uk> wrote in message
> news:a1o2h1$sfe3f$5@ID-25716.news.dfncis.de...
> > However, really, a much better design is like this:
>
> No, because protected operations can't make blocking calls.
>
> Note that all predefined I/O packages are "potentially blocking," which
> means, for example, that you can't call Text_IO from inside a protected
> object.

Well, I had no idea that Dmitry's critical section contained blocking calls.
My comments were made on the very reasonable assumption that it didn't. I
would suggest that it is unusual for a critical section to need to make
blocking calls, and generally desirable to remove blocking calls from
critical sections wherever possible. Moreover, it is generally desirable to
reduce the execution time (both average and maximum, if possible) of a
critical section to a reasonable minimum.

> So you still need a semaphore-style type (as in the original post), to
> handle the situation in which blocking calls are made inside a critical
> section.

No you don't. If your critical section must perform a blocking action, put
it in a task. E.g.:

  task type My_Resource_Type is
    entry Do_Something_Critical;
  end;

  ...

  task body My_Resource_Type is
    ... -- resource state
  begin
    loop
      -- internal pre-processing
      select
        accept Do_Something_Critical do
          -- critical code here (blocking operations possible)
        exception
          when others =>
            -- reset resource state to something stable
        end;
        -- internal (checking and) post-processing
      or
        terminate;
      end select;
    end loop;
  end My_Resource_Type;

  ...

  The_Resource: My_Resource_Type;

  ...

  begin
    -- pre-processing specific to this point in execution
    The_Resource.Do_Something_Critical;
    -- post-processing specific to this point in execution
  end;

Of course, this is likely to be implemented by the compiler using
mutexes/semaphores. The point is, it's generally better to use a
higher-level construct like this, because it generally gives the compiler
more opportunity to catch bugs.

> The technique shown using a Limited_Controlled type to
> automatically seize and release the semaphore is a standard Ada95 idiom.

It may be a standard idiom, to some people, but that doesn't necessarily
make it the best approach. When the COBOL ALTER verb was invented, it was
standard idiom at the time for programs to modify their own code
dynamically. Nowadays, it is recognised that anyone using ALTER faces the
danger of self-lobotomy*.

Or, to put it another way, the standard idiom in this case doesn't seem to
address the problem of an exception leaving the resource in question in an
unstable state. Nor does it do anything to mitigate the effects of
accidentally omitting (or wrongly positioning) one of the seize or release
calls.

--
Best wishes,
Nick Roberts




*Eight months, nine hundred and fifty man hours, twenty three bottles of
painkillers, ninety five bottles of whiskey, two divorces, one and a half
garagefuls of listings, seventy severe arguments with the boss, and five
therapists later.

Or so ex CA Associates employees tell me, anyway.

:-o ;-)






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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-13  5:45     ` Nick Roberts
@ 2002-01-13  8:21       ` tmoran
  2002-01-13 16:12         ` Nick Roberts
  2002-01-13 15:08       ` Simon Wright
                         ` (2 subsequent siblings)
  3 siblings, 1 reply; 41+ messages in thread
From: tmoran @ 2002-01-13  8:21 UTC (permalink / raw)


> and generally desirable to remove blocking calls from
> critical sections wherever possible.
  Sometimes the "critical section" quite reasonably could last a
long time - when it's doing an IO operation using an IO channel,
for instance.  That can be long, lots of other things can legitimately
run, but there better not be anyone else trying to use that same IO
channel at the same time.  Claw.Sockets.Read, for instance, uses
this idiom, and a Read could block a particular socket for quite a while.

>address the problem of an exception leaving the resource in question in an
>unstable state. Nor does it do anything to mitigate the effects of
>accidentally omitting (or wrongly positioning) one of the seize or release
>calls.
  The original question was about:
>   Temp : Lock (Mutex_of_a_resource'Access);
>begin
>   ...  -- Safe access to the resource
>end; -- Mutex is released even if an exception propagates
  and clearly there is no danger of omitting or wrongly positioning
anything, or of problems with an exception.  In fact

   Temp : Lock (Mutex_of_a_resource'Access);
   Something : Positive := Some_Value;

would even be safe if Some_Value was negative, which is not a case a
local (after the "begin") exception handler would catch.  Once
Initialize(Temp) has been called a matching Finalize(Temp) call is
guaranteed.

> > The technique shown using a Limited_Controlled type to
> > automatically seize and release the semaphore is a standard Ada95 idiom.
  And a good idiom, IMHO.



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-13  5:45     ` Nick Roberts
  2002-01-13  8:21       ` tmoran
@ 2002-01-13 15:08       ` Simon Wright
  2002-01-15 17:53         ` Nick Roberts
  2002-01-13 16:51       ` Jeffrey Carter
  2002-01-14 23:32       ` Matthew Heaney
  3 siblings, 1 reply; 41+ messages in thread
From: Simon Wright @ 2002-01-13 15:08 UTC (permalink / raw)


"Nick Roberts" <nickroberts@adaos.worldonline.co.uk> writes:

> Or, to put it another way, the standard idiom in this case doesn't
> seem to address the problem of an exception leaving the resource in
> question in an unstable state. Nor does it do anything to mitigate
> the effects of accidentally omitting (or wrongly positioning) one of
> the seize or release calls.

?

critical : declare
   l : Lock;
begin
   critical stuff
end critical;

* No seize/release calls

* Finalization on exit from critical region for whatever reason
  (including exceptions)



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-13  8:21       ` tmoran
@ 2002-01-13 16:12         ` Nick Roberts
  0 siblings, 0 replies; 41+ messages in thread
From: Nick Roberts @ 2002-01-13 16:12 UTC (permalink / raw)


<tmoran@acm.org> wrote in message
news:msb08.31576$%d4.3960804474@newssvr14.news.prodigy.com...

> > and generally desirable to remove blocking calls from
> > critical sections wherever possible.
>   Sometimes the "critical section" quite reasonably could last a
> long time - when it's doing an IO operation using an IO channel,
> for instance.  That can be long, lots of other things can legitimately
> run, but there better not be anyone else trying to use that same IO
> channel at the same time.  Claw.Sockets.Read, for instance, uses
> this idiom, and a Read could block a particular socket for quite a while.

Yes, but this isn't really a point at issue, is it?

The situation you describe is a classic example of (the need for) the use of
a buffer (is the terminology 'concurrent queue' these days?) to smooth out
the timing differences between producer and consumer. If the target is a
multi-processor machine, it can be particularly important (from the point of
view of efficiency) that a task is used to do the buffering.

> >address the problem of an exception leaving the resource in question in
an
> >unstable state. Nor does it do anything to mitigate the effects of
> >accidentally omitting (or wrongly positioning) one of the seize or
release
> >calls.
>   The original question was about:
> >   Temp : Lock (Mutex_of_a_resource'Access);
> >begin
> >   ...  -- Safe access to the resource
> >end; -- Mutex is released even if an exception propagates
>   and clearly there is no danger of omitting or wrongly positioning
> anything,

Absolutely true. My mistake.

> or of problems with an exception.

But there *is* a problem with exception handling: whilst the mutex will be
released okay, the resource could be left in a bad state, and nothing (in
Dmitry's example) is done to attempt to repair the damage.

In fact, I note the possibility of building into the Finalize checks for the
integrity of the resource's state (upon failure of which the resource could
be reset). This technique certainly could mitigate the problems of leaving
the resource in an unstable state, but it may be impractical or too
inefficient. It could also be obfuscatory, in placing the clean-up code away
from the code it cleans up.

The advantage of having the clean-up code at the site of each (call to a)
critical section (as per my examples) is that the clean-up code can then be
specific to the code which causes the damage, and this is likely to be the
more intelligible arrangement too.

> In fact
>
>    Temp : Lock (Mutex_of_a_resource'Access);
>    Something : Positive := Some_Value;
>
> would even be safe if Some_Value was negative,
> ...
> Once Initialize(Temp) has been called a matching Finalize(Temp) call is
> guaranteed.

Correct (apart from the possibility of the resource being left in a bad
state, as just mentioned).

> which is not a case a
> local (after the "begin") exception handler would catch.

Correct, but irrelevant.

In my first example (with explicit seize and release), an exception
propagated during elaboration of the relevant declarative region (where Lock
is) would prevent the mutex ever being seized. In my second example (with
the protected object holding the resource state), an exception propagated
during elaboration of the declarative region of the protected procedure
Do_Something_Critical would be propagated out of the procedure before it
could possibly start altering the state of the resource (and the protected
object is internally unlocked when the exception is propagated out). In my
third example (with the task type), a call on the entry
Do_Something_Critical does not cause the elaboration of a declarative region
(but if it did, it too would be placed outside the actual critical code).
Thus, these are all three perfectly safe from something going wrong during
the elaboration of declarations.

> > > The technique shown using a Limited_Controlled type to
> > > automatically seize and release the semaphore is a standard Ada95
idiom.
>   And a good idiom, IMHO.

I continue to disagree, for the reasons I have stated.

I recognise that there will sometimes be occasions when the lower-level
approach cannot be avoided (as with other situations where there is a choice
between higher and lower levels of solution), but I hold that such occasions
will be rare. (I also recognise that my O is rarely H ;-)

--
Best wishes,
Nick Roberts






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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-13  5:45     ` Nick Roberts
  2002-01-13  8:21       ` tmoran
  2002-01-13 15:08       ` Simon Wright
@ 2002-01-13 16:51       ` Jeffrey Carter
  2002-01-14 23:32       ` Matthew Heaney
  3 siblings, 0 replies; 41+ messages in thread
From: Jeffrey Carter @ 2002-01-13 16:51 UTC (permalink / raw)


Nick Roberts wrote:
> 

[referring to using a task to implement a critical section]

> Of course, this is likely to be implemented by the compiler using
> mutexes/semaphores.

This appears to have been the expectation of the Ada-83 designers, but
few if any compilers did so automatically (some had pragmata to allow
the developer to indicate that a task was really a monitor), which is
why protected objects were added to Ada 95.

Nevertheless, I would still prefer the use of a task to a semaphore when
mutual exclusion is needed for processing that cannot be in a protected
object.

-- 
Jeff Carter
"Perfidious English mouse-dropping hoarders."
Monty Python & the Holy Grail



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-12 22:04   ` Matthew Heaney
  2002-01-13  5:45     ` Nick Roberts
@ 2002-01-14  8:31     ` Jean-Pierre Rosen
  1 sibling, 0 replies; 41+ messages in thread
From: Jean-Pierre Rosen @ 2002-01-14  8:31 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 1096 bytes --]


"Matthew Heaney" <mheaney@on2.com> a �crit dans le message news: u41cg9jaqnrs56@corp.supernews.com...
>
> "Nick Roberts" <nickroberts@adaos.worldonline.co.uk> wrote in message
> news:a1o2h1$sfe3f$5@ID-25716.news.dfncis.de...
> > However, really, a much better design is like this:
>
> No, because protected operations can't make blocking calls.
>
> Note that all predefined I/O packages are "potentially blocking," which
> means, for example, that you can't call Text_IO from inside a protected
> object.
>
> So you still need a semaphore-style type (as in the original post), to
> handle the situation in which blocking calls are made inside a critical
> section.  The technique shown using a Limited_Controlled type to
> automatically seize and release the semaphore is a standard Ada95 idiom.
>
Have a look at package Protection from Adalog's components page (http://www.adalog.fr/compo2.htm) for a component that automates
this idiom.

--
---------------------------------------------------------
           J-P. Rosen (rosen@adalog.fr)
Visit Adalog's web site at http://www.adalog.fr





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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-11 19:40 ` Robert Dewar
  2002-01-12 10:18   ` Martin Dowie
@ 2002-01-14  8:54   ` Dmitry A. Kazakov
  1 sibling, 0 replies; 41+ messages in thread
From: Dmitry A. Kazakov @ 2002-01-14  8:54 UTC (permalink / raw)


On 11 Jan 2002 11:40:37 -0800, dewar@gnat.com (Robert Dewar) wrote:

>dmitry@elros.cbb-automation.de (Dmitry A. Kazakov) wrote in message news:<3c3ee8c8.105408250@News.CIS.DFN.DE>...
>> The problem is that the object Temp is never referenced. 
>> The compiler
>> complains of that, but it is a minor problem. The 
>> questions are
>
>
>In GNAT, we just introduced 
>
>   pragma Unreferenced (entity-name, entity-name, ....);
>
>specifically to deal with entities that are deliberately
>unreferenced. This pragma supresses unreferenced warnings
>for the entity, and gives an error if the entity is actually referenced.
>
>It provides useful documentation that the entity is really
>really intended to be there, and serves a purpose even
>though it is not specifically referenced.

Very nice. In which GNAT version it appears?

Regards,
Dmitry Kazakov



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-12  1:11 ` Nick Roberts
  2002-01-12 22:04   ` Matthew Heaney
@ 2002-01-14  9:42   ` Dmitry A. Kazakov
  2002-01-15 15:41     ` Matthew Heaney
  2002-01-15 18:59     ` Nick Roberts
  1 sibling, 2 replies; 41+ messages in thread
From: Dmitry A. Kazakov @ 2002-01-14  9:42 UTC (permalink / raw)


On Sat, 12 Jan 2002 01:11:43 -0000, "Nick Roberts"
<nickroberts@adaos.worldonline.co.uk> wrote:

[...]

>The general idea is that a mutex is a low-level synchronisation construct,
>and Ada provides a higher-level construct (protected objects) that takes
>away much of the pain and danger of using mutexes directly.

Absolutely. So my suspicion goes. Or else, there must be something
fundamentally wrong [in design] if unused objects are *really*
required. It clearly indicates a *problem*.

I would prefer a monitor based solution as Jeffrey Carter suggested,
but I just failed to design it [properly].

Maybe I should describe the problem. There is a communication channel
implemented as a tagged type. The base type provides low-level I/O
operations. It has read and write tasks serving as monitors. The
problem is with the derived types [potentially an arbitrary number of
them]. They extend the base type and  provide layered high-level
operations. Some of them must lock the channel [either read or write
one]. A "natural" solution would be to extend monitors, but alas tasks
are not tagged. An untagged solution requires an increasing number of
wrapping subroutines [thus run-time penalty]. A genericity-based
solution produces a combinatoric explosion of packages used as the
parameters for other packages etc. They must be instantiated at the
library level. In other words it is a mess.

So I chose the lesser evil = mutexes with all that nasy problems of
error recovery, you have pointed. I really do not like it.

Regards,
Dmitry Kazakov



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-13  5:45     ` Nick Roberts
                         ` (2 preceding siblings ...)
  2002-01-13 16:51       ` Jeffrey Carter
@ 2002-01-14 23:32       ` Matthew Heaney
  2002-01-15  8:53         ` Dmitry A. Kazakov
  3 siblings, 1 reply; 41+ messages in thread
From: Matthew Heaney @ 2002-01-14 23:32 UTC (permalink / raw)



"Nick Roberts" <nickroberts@adaos.worldonline.co.uk> wrote in message
news:a1r6tr$slh9n$1@ID-25716.news.dfncis.de...
> Well, I had no idea that Dmitry's critical section contained blocking
calls.

You seem to be objecting to the very idea of a semaphore type, claiming that
all your synchronization needs can be done using a monitor-style protected
object.  That would be false, because a monitor implemented as a protected
object is not allowed to make blocking calls.


> My comments were made on the very reasonable assumption that it didn't. I
> would suggest that it is unusual for a critical section to need to make
> blocking calls,

No, it is very common to have to make blocking calls in critical region.
For example, I am writing a server in which client messages are logged to a
file.  However, multiple threads can be handling client requests, so the
manipulation of the file object (to write the message) must by protected by
a critical region.

The critical region cannot be implemented as a monitor-style protected
object, because the action is blocking.  Hence the need for a
semaphore-style protected object.

> and generally desirable to remove blocking calls from critical sections
wherever possible.

I don't make blocking calls inside a critical region for my own amusement.
I make a blocking call inside a critical region because I need to serialize
access to a resource, and manipulation of the resource blocks.


> Moreover, it is generally desirable to
> reduce the execution time (both average and maximum, if possible) of a
> critical section to a reasonable minimum.

Motherhood and apple pie: no kidding.


> No you don't. If your critical section must perform a blocking action, put
> it in a task. E.g.:

But the whole point is to use tasks as a unit of concurrency, not as a
synchronization mechanism.  That's why protected objects were invented.


> The point is, it's generally better to use a
> higher-level construct like this, because it generally gives the compiler
> more opportunity to catch bugs.

Ever hear the story about the king who asked both a computer programmer and
an electrical engineer to design a toaster?

http://www.shartwell.freeserve.co.uk/humor-site/elecvscomp.htm

To use a high-level construct like a task to do a job that only requires a
low-level primitive is an example of "abstraction inversion."

Yes, a semaphore all by itself is dangerous, because you can forget to
release it.  But when used in conjuction with a controlled object, the
release is guaranteed to happen, so there is no danger.


> It may be a standard idiom, to some people, but that doesn't necessarily
> make it the best approach.

All you're saying here is that you don't like it.

> Or, to put it another way, the standard idiom in this case doesn't seem to
> address the problem of an exception leaving the resource in question in an
> unstable state.

Nor was it intended to.  The idiom exists to guarantee that the Release
operation of a semaphore is called, not matter what happens.  If the
exception leaves the resource in an "unknown state," that is a separate
issue, quite orthogonal to the issue of synchronization.


> Nor does it do anything to mitigate the effects of
> accidentally omitting (or wrongly positioning) one of the seize or release
> calls.

Huh?  When you use the controlled-object idiom, the seize and release calls
are made for you by the controlled-object:

Semphore : aliased Semaphore_Type;

Critical_Section:
declare
   Control : Control_Type (Semaphore'Access);
begin
  <manipulate resource>
end Critical_Section;


The real problem is that Ada doesn't have a termination clause, like a
finally clause in Java or in Win32 SEH.
That's why these idioms are necessary: to work around that omission.









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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-14 23:32       ` Matthew Heaney
@ 2002-01-15  8:53         ` Dmitry A. Kazakov
  0 siblings, 0 replies; 41+ messages in thread
From: Dmitry A. Kazakov @ 2002-01-15  8:53 UTC (permalink / raw)


On Mon, 14 Jan 2002 18:32:35 -0500, "Matthew Heaney" <mheaney@on2.com>
wrote:

>Yes, a semaphore all by itself is dangerous, because you can forget to
>release it.  But when used in conjuction with a controlled object, the
>release is guaranteed to happen, so there is no danger.

IMO, when it is said that semaphore is a low-level mechanism, it means
approximately the following. Tasks and protected objects may have
entries implementing *something* [useful (:-))]. In contrary to this,
semaphores just implement themselves. A semaphore is useless without
the resource it blocks. But then why is it separated from the
resource?

Regards,
Dmitry Kazakov



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-14  9:42   ` Dmitry A. Kazakov
@ 2002-01-15 15:41     ` Matthew Heaney
  2002-01-15 16:18       ` Hyman Rosen
                         ` (2 more replies)
  2002-01-15 18:59     ` Nick Roberts
  1 sibling, 3 replies; 41+ messages in thread
From: Matthew Heaney @ 2002-01-15 15:41 UTC (permalink / raw)



"Dmitry A. Kazakov" <dmitry@elros.cbb-automation.de> wrote in message
news:3c429d1c.2624281@News.CIS.DFN.DE...
> Absolutely. So my suspicion goes. Or else, there must be something
> fundamentally wrong [in design] if unused objects are *really*
> required. It clearly indicates a *problem*.

The problem is that the language doesn't have a termination handler for
blocks, a la a "finally" clause in Java or Win32 Structured Exception
Handling.  So you have to create "unused" objects as a work-around.


> A "natural" solution would be to extend monitors, but alas tasks
> are not tagged.

Can't you parameterize a task with an access discriminant that binds to an
object whose type is class-wide, and then call (dispatching) operations on
that object?

type T is tagged limited null record;
procedure Op (O : access T);

task Task_Object (O : access T'Class) is ..

task body Task_Object is
begin
  ...
  Op (O);  --dispatching call to object viewed thru access discrim
  ...
end;

Now  you're free to derive types from T, and bind derived type instances to
a task.  Using this idiom obviates the need for extensible task types.


> So I chose the lesser evil = mutexes with all that nasty problems of
> error recovery, you have pointed. I really do not like it.

The language gives you primitives so that you can use to build in the
safety.  There is no problem using semaphores in Ada95, if you follow
certain idioms.








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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-15 15:41     ` Matthew Heaney
@ 2002-01-15 16:18       ` Hyman Rosen
  2002-01-15 16:57       ` Darren New
  2002-01-16 15:18       ` Dmitry A. Kazakov
  2 siblings, 0 replies; 41+ messages in thread
From: Hyman Rosen @ 2002-01-15 16:18 UTC (permalink / raw)


Matthew Heaney wrote:

> The problem is that the language doesn't have a termination handler for
> blocks, a la a "finally" clause in Java or Win32 Structured Exception
> Handling.  So you have to create "unused" objects as a work-around.


In C++, this is considered a desirable design paradigm, not a problem.
If you happen to be controlling several resources, the "finally" way
marches off the right side of the page -

	try {
		// acquire resorce 1
		try {
			// acquire resource 2
			try {
				// acquire resource 3
				// now do something
			} finally {
				// release resource 3
			}
		} finally {
			// release resource 2
		}
	} finally {
		// release resource 1
	}

As opposed to

	Res1Holder r1; // acquire resource 1
	Res2Holder r2; // acquire resource 2
	Res3Holder r3; // acquire resource 3

	// do something




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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-15 15:41     ` Matthew Heaney
  2002-01-15 16:18       ` Hyman Rosen
@ 2002-01-15 16:57       ` Darren New
  2002-01-15 18:57         ` Matthew Heaney
  2002-01-16 15:18       ` Dmitry A. Kazakov
  2 siblings, 1 reply; 41+ messages in thread
From: Darren New @ 2002-01-15 16:57 UTC (permalink / raw)


Matthew Heaney wrote:
> Now  you're free to derive types from T, and bind derived type instances to
> a task.  Using this idiom obviates the need for extensible task types.

Not really, because there are various operations that may only be
applied to entries, and various operations that may only appear in task
bodies and hence your T and its derived types cannot affect it.

As an example, if your task is coded without any guards on the entries,
there's no way for a child of T to add a guard.

You can do a fair amount with this idiom, but I wouldn't say it
*obviates* the need for extensible tasks.

-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
  The opposite of always is sometimes.
   The opposite of never is sometimes.



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-13 15:08       ` Simon Wright
@ 2002-01-15 17:53         ` Nick Roberts
  0 siblings, 0 replies; 41+ messages in thread
From: Nick Roberts @ 2002-01-15 17:53 UTC (permalink / raw)


"Simon Wright" <simon@pushface.org> wrote in message
news:x7v7kqmxp8j.fsf@smaug.pushface.org...

> ...
> * No seize/release calls
>
> * Finalization on exit from critical region for whatever reason
>   (including exceptions)

Yes, quite right, I slipped. See my other reply.

--
Best wishes,
Nick Roberts






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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-15 16:57       ` Darren New
@ 2002-01-15 18:57         ` Matthew Heaney
  2002-01-16  0:57           ` Darren New
  0 siblings, 1 reply; 41+ messages in thread
From: Matthew Heaney @ 2002-01-15 18:57 UTC (permalink / raw)



"Darren New" <dnew@san.rr.com> wrote in message
news:3C445F34.44697AEF@san.rr.com...
> Matthew Heaney wrote:
> > Now  you're free to derive types from T, and bind derived type instances
to
> > a task.  Using this idiom obviates the need for extensible task types.
>
> Not really, because there are various operations that may only be
> applied to entries, and various operations that may only appear in task
> bodies and hence your T and its derived types cannot affect it.

But won't this work:

package P is
   type T is abstract tagged limited private;
   procedure Op (O : in out T) is abstract;
private
   task type T_Task_Type (O : access T) is
      entry E;
   end T_Task_Type;

   type T is abstract tagged limited record
      T_Task : T_Task_Type (T'Access);
   end record;

end P;

Now each instance of T gets its own thread.  Types in T'Class have
visibility to the task object (assuming they are declared in children of P).


> As an example, if your task is coded without any guards on the entries,
> there's no way for a child of T to add a guard.

But couldn't a derived type NT extend T to include a protected object (that
implements a semaphore, say), that could be used by types in NT'Class to do
the same thing?


> You can do a fair amount with this idiom, but I wouldn't say it
> *obviates* the need for extensible tasks.

I haven't found a need for extensible tasks.







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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-14  9:42   ` Dmitry A. Kazakov
  2002-01-15 15:41     ` Matthew Heaney
@ 2002-01-15 18:59     ` Nick Roberts
  2002-01-16 15:05       ` Dmitry A. Kazakov
  1 sibling, 1 reply; 41+ messages in thread
From: Nick Roberts @ 2002-01-15 18:59 UTC (permalink / raw)


"Dmitry A. Kazakov" <dmitry@elros.cbb-automation.de> wrote in message
news:3c429d1c.2624281@News.CIS.DFN.DE...

> ...
> Maybe I should describe the problem. There is a communication channel
> implemented as a tagged type. The base type provides low-level I/O
> operations. It has read and write tasks serving as monitors. The
> problem is with the derived types [potentially an arbitrary number of
> them]. They extend the base type and  provide layered high-level
> operations.

I'm not too keen on this approach, because this way the higher levels do not
'hide away' the lower-level interface. Normally, it would be better for a
distinctly higher-level interface to be based on a different type (not a
derived one).

> Some of them must lock the channel [either read or write
> one]. A "natural" solution would be to extend monitors, but alas tasks
> are not tagged.

But the tagged type could contain a component of a task type.

> An untagged solution requires an increasing number of
> wrapping subroutines [thus run-time penalty].

I really don't understand why there should be a greater time penalty for
this solution. I think possibly this is a misunderstanding that lies at the
heart of your problems.

> A genericity-based
> solution produces a combinatoric explosion of packages used as the
> parameters for other packages etc. They must be instantiated at the
> library level. In other words it is a mess.

I don't really see generics being directly relevant to the problem here.

> So I chose the lesser evil = mutexes with all that nasy problems of
> error recovery, you have pointed. I really do not like it.

Please find below an outline skeleton of what a simple communications
hierarchy might look like in Ada. It is merely a guess at what you need, and
it even then is merely a suggestion of how to do it.

=====
-- Abstract level: hardware devices in general

with Ada.Finalization;
package Devices is
   type Abstract_Device is abstract Limited_Controlled with private;
   procedure Reset (Device: access Abstract_Device) is abstract;
   ...
private
   ...
end Devices;

=====
-- Abstract level: communications devices

package Devices.Communications is
   type Byte_Array is ...;
   type Communication_Device is abstract new Abstract_Device with private;
   procedure Read (Transceiver: access Communication_Device;
                   Data: out Byte_Array;
                   Last: out Natural) is abstract;
   procedure Write (Transceiver: access Communication_Device;
                    Data: in Byte_Array) is abstract;
   ...
private
   ...
end Devices.Communications;

=====
-- Low concrete level: my particular communications hardware

with Devices.Communications;
with My_Stuff.Hardware;
package My_Stuff.My_Comms is
   type My_Comms_Device_X is
      new Devices.Communications.Comuunications_Device with private;
   ...
private
   task type Reception_Monitor is
      entry Read (Data: ...);
      entry Reset (Port: in My_Stuff.Hardware.Port_Number);
      entry Stop;
   end;
   task type Transmission_Monitor is
      entry Write (Data: ...);
      entry Reset (Port: in My_Stuff.Hardware.Port_Number);
      entry Stop;
   end;
   ... etc
   type Communications_Device is abstract new Abstract_Device with
      record
         R_Mon: Reception_Monitor;
         T_Mon: Transmission_Monitor;
         ...
      end record;
   procedure Reset (...);
   procedure Read (...);
   procedure Write (...);
   procedure Initialize (...);
   procedure Finalize (...);
   ...
end My_Stuff.My_Comms;

=====
-- Higher level: a communications facility for application programs

with My_Stuff.My_Comms;
with Ada.Streams.Stream_IO;
package Utility.Intercom is
   type Socket_Type is limited private;
   procedure Open (Socket: in out Socket_Type;
                   Name:   in     String;
                   Form:   in     String := "");
   procedure Reset (Socket: in out Socket_Type);
   procedure Close (Socket: in out Socket_Type);
   function Input_Stream  (Socket: in Socket_Type)
               return Ada.Streams.Stream_IO.Stream_Access;
   function Output_Stream (Socket: in Socket_Type)
               return Ada.Streams.Stream_IO.Stream_Access;
private
   ...
end Utility.Intercom;

=====

Note the embedding of two tasks, R_Mon and T_Mon, acting as input and output
monitor respectively, as components within a tagged type.

These tasks would be programmed to buffer the byte stream passing through
them, so as to help 'smooth out the bumps' that you inevitably get as
differences in the rate at which other tasks will produce or consume data.

On a single-processor system, they will act like passive synchronisation
mechanisms (but that's okay); on a multi-processor system they could
actually help to increase the efficiency (speed) of the overall system,
possibly quite dramatically, because they can be busily receiving and
buffering data (R_Mon) and sending buffered data (T_Mon) while other tasks
are busy doing other things.

Also notice how I use successive derivations from a tagged type to evolve
from an abstract level to a concrete one (where the theme is promises made
and promises kept), but I use different types to evolve from a low level to
a higher one (where the theme is the progressive hiding away of nasty
nitty-gritty details).

Hope this helps in some way.

--
Best wishes,
Nick Roberts






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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-15 18:57         ` Matthew Heaney
@ 2002-01-16  0:57           ` Darren New
  2002-01-16 16:35             ` Stephen Leake
  2002-01-16 23:04             ` Matthew Heaney
  0 siblings, 2 replies; 41+ messages in thread
From: Darren New @ 2002-01-16  0:57 UTC (permalink / raw)


Matthew Heaney wrote:
> 
> "Darren New" <dnew@san.rr.com> wrote in message
> news:3C445F34.44697AEF@san.rr.com...
> > Matthew Heaney wrote:
> > > Now  you're free to derive types from T, and bind derived type instances
> to
> > > a task.  Using this idiom obviates the need for extensible task types.
> >
> > Not really, because there are various operations that may only be
> > applied to entries, and various operations that may only appear in task
> > bodies and hence your T and its derived types cannot affect it.
> 
> But won't this work:
> 
> package P is
>    type T is abstract tagged limited private;
>    procedure Op (O : in out T) is abstract;
> private
>    task type T_Task_Type (O : access T) is
>       entry E;
>    end T_Task_Type;
> 
>    type T is abstract tagged limited record
>       T_Task : T_Task_Type (T'Access);
>    end record;
> 
> end P;
> 
> Now each instance of T gets its own thread.  Types in T'Class have
> visibility to the task object (assuming they are declared in children of P).

Sure. Now Op can't act as an entry.

The basic problem is that if I have something that says 
  x.boop
and boop is an entry, it can only call one specific piece of code. That
one specific piece of code has to be the one that determines whether the
code blocks.


> > As an example, if your task is coded without any guards on the entries,
> > there's no way for a child of T to add a guard.
> 
> But couldn't a derived type NT extend T to include a protected object (that
> implements a semaphore, say), that could be used by types in NT'Class to do
> the same thing?

That's not guarding an entry. If T.op dispatches, then it can't be an
entry. If it's an entry, I can't override it.

I can't do
  select T.op or delay 10.0 ; yadda end select;
and have T.op be one of two different pieces of code, for example.

The problem I had was trying to build a library wherein the framework
instantiates a number of tasks. Each task is provided by the user of the
framework. (Think, for example, of someone writing multiple types of
windows in a GUI framework.) Each task would need to respond to
different events at different times (meaning selective accepts in the
tasks) and the framework wanted to not have to know what all these
different task types are (meaning dispatching). Couldn't figure out how
to do it properly. Wound up building a kludge that basically involved
manually marshalling all the messages and queueing them, and generally
reimplementing the entry queue mechanism in Ada.

> I haven't found a need for extensible tasks.

I have, but then that's the kind of software I write.

-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
  The opposite of always is sometimes.
   The opposite of never is sometimes.



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-15 18:59     ` Nick Roberts
@ 2002-01-16 15:05       ` Dmitry A. Kazakov
  2002-01-16 18:30         ` Matthew Heaney
                           ` (2 more replies)
  0 siblings, 3 replies; 41+ messages in thread
From: Dmitry A. Kazakov @ 2002-01-16 15:05 UTC (permalink / raw)


On Tue, 15 Jan 2002 18:59:16 -0000, "Nick Roberts"
<nickroberts@adaos.worldonline.co.uk> wrote:

>"Dmitry A. Kazakov" <dmitry@elros.cbb-automation.de> wrote in message
>news:3c429d1c.2624281@News.CIS.DFN.DE...
>
>> ...
>> Maybe I should describe the problem. There is a communication channel
>> implemented as a tagged type. The base type provides low-level I/O
>> operations. It has read and write tasks serving as monitors. The
>> problem is with the derived types [potentially an arbitrary number of
>> them]. They extend the base type and  provide layered high-level
>> operations.
>
>I'm not too keen on this approach, because this way the higher levels do not
>'hide away' the lower-level interface. Normally, it would be better for a
>distinctly higher-level interface to be based on a different type (not a
>derived one).

Well, you can derive from the base in the private part and expose
operations you want to keep visible using wrappers implemented via
renaming in the body.

However, I always wished Ada having an ability to disallow primitive
operations. Like:

type Unordered is new Integer;
function ">" (Left, Right : Unordered) is abstract; -- Disallow ">"

>> Some of them must lock the channel [either read or write
>> one]. A "natural" solution would be to extend monitors, but alas tasks
>> are not tagged.
>
>But the tagged type could contain a component of a task type.

Yes, and another problem as well. Finalize is called *after* all task
components has been terminated. So when the object is being destroyed
you cannot notify tasks about that. You must use pointers to tasks if
you need a "prepare-to-die" notification.

>> An untagged solution requires an increasing number of
>> wrapping subroutines [thus run-time penalty].
>
>I really don't understand why there should be a greater time penalty for
>this solution. I think possibly this is a misunderstanding that lies at the
>heart of your problems.

Maybe the penalty is not very high, but writting dozens of wrappers is
disgusting.

>> A genericity-based
>> solution produces a combinatoric explosion of packages used as the
>> parameters for other packages etc. They must be instantiated at the
>> library level. In other words it is a mess.
>
>I don't really see generics being directly relevant to the problem here.

You can make package implementing some interface layer generic with a
lower level package as the formal parameter.

>> So I chose the lesser evil = mutexes with all that nasy problems of
>> error recovery, you have pointed. I really do not like it.
>
>Please find below an outline skeleton of what a simple communications
>hierarchy might look like in Ada. It is merely a guess at what you need, and
>it even then is merely a suggestion of how to do it.

[ driver example snipped ]

The difference is that with a driver you always know the interface:
Open/Read/Write/Close. The protocols we must implement are more
complex. Additionally one should have an ability to replace any part
of the hierarchy. For instance, low level: transport [like sockets vs.
RS323], middle level: time synchronization protocol, high level client
vs. server. Of course this ideal is reachless, so I am considering a
which "local optimum" to take. (:-))

Regards,
Dmitry Kazakov



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-15 15:41     ` Matthew Heaney
  2002-01-15 16:18       ` Hyman Rosen
  2002-01-15 16:57       ` Darren New
@ 2002-01-16 15:18       ` Dmitry A. Kazakov
  2 siblings, 0 replies; 41+ messages in thread
From: Dmitry A. Kazakov @ 2002-01-16 15:18 UTC (permalink / raw)


On Tue, 15 Jan 2002 10:41:58 -0500, "Matthew Heaney" <mheaney@on2.com>
wrote:

>> So I chose the lesser evil = mutexes with all that nasty problems of
>> error recovery, you have pointed. I really do not like it.
>
>The language gives you primitives so that you can use to build in the
>safety.  There is no problem using semaphores in Ada95, if you follow
>certain idioms.

There is no harm in gotos if you know where you go. (:-))

I believe that like in case of gotos, use of semaphores should be
minimized [if possible, of course].

Regards,
Dmitry Kazakov



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-16  0:57           ` Darren New
@ 2002-01-16 16:35             ` Stephen Leake
  2002-01-16 18:07               ` Darren New
  2002-01-16 23:04             ` Matthew Heaney
  1 sibling, 1 reply; 41+ messages in thread
From: Stephen Leake @ 2002-01-16 16:35 UTC (permalink / raw)


Darren New <dnew@san.rr.com> writes:

> > > As an example, if your task is coded without any guards on the entries,
> > > there's no way for a child of T to add a guard.
> > 
> > But couldn't a derived type NT extend T to include a protected object (that
> > implements a semaphore, say), that could be used by types in NT'Class to do
> > the same thing?
> 
> That's not guarding an entry. If T.op dispatches, then it can't be an
> entry. If it's an entry, I can't override it.

Hmm. Entries with guards only occur in protected objects, not tasks
(unless I'm really confused). So I'll assume we are talking about
protected objects here.

It's true that an entry (in either a task or a protected object) can
be dispatching. But you can provide a guard in the parent task, that
uses a function that is dispatching:

entry Foo (...) when T.Bar;

This appears to be legal when T.Bar is dispatching, but I've never
tried it.

> I can't do select T.op or delay 10.0 ; yadda end select; and have
> T.op be one of two different pieces of code, for example.

True.

> The problem I had was trying to build a library wherein the
> framework instantiates a number of tasks. Each task is provided by
> the user of the framework. (Think, for example, of someone writing
> multiple types of windows in a GUI framework.) Each task would need
> to respond to different events at different times (meaning selective
> accepts in the tasks) and the framework wanted to not have to know
> what all these different task types are (meaning dispatching).
> Couldn't figure out how to do it properly. Wound up building a
> kludge that basically involved manually marshalling all the messages
> and queueing them, and generally reimplementing the entry queue
> mechanism in Ada.

Maybe you can invert this; define the tasks in the framework code,
with a standard set of entries, but have each entry call a function
via dispatching, and allow the user to override those functions. This
is the structure used by Borland's Object Windows Library.

> > I haven't found a need for extensible tasks.
> 
> I have, but then that's the kind of software I write.

To be precise, you have a need for dispatching in a tasking context,
and tried a design approach that doesn't work in Ada. There are other
approaches; if one of them works, you have not found a need for
extensible tasks :).

-- 
-- Stephe



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-16 16:35             ` Stephen Leake
@ 2002-01-16 18:07               ` Darren New
  2002-01-16 23:18                 ` Matthew Heaney
  0 siblings, 1 reply; 41+ messages in thread
From: Darren New @ 2002-01-16 18:07 UTC (permalink / raw)


Stephen Leake wrote:
> Hmm. Entries with guards only occur in protected objects, not tasks
> (unless I'm really confused). So I'll assume we are talking about
> protected objects here.

Errr, no. I'm pretty sure a selective accept works in tasks. 

> It's true that an entry (in either a task or a protected object) can
> be dispatching. But you can provide a guard in the parent task, that
> uses a function that is dispatching:
> 
> entry Foo (...) when T.Bar;
> 
> This appears to be legal when T.Bar is dispatching, but I've never
> tried it.

Yes. But the problem is in the callee you want a selective accept, and
in the caller you want a conditional entry, and you don't want those two
tied together at compile time. In that case, you're screwed. If you
could derive a task type from another task type, I'd be fine; that is,
if I could have multiple different task bodies for the same task type,
I'd not have had even conceptual problems.

> Maybe you can invert this; define the tasks in the framework code,
> with a standard set of entries, but have each entry call a function
> via dispatching, and allow the user to override those functions. This
> is the structure used by Borland's Object Windows Library.

Basically, what I wound up doing was saying "I'll dispatch to your
objects, and you must not block. If you block, you will wedge up every
task trying to do I/O. Caveat emptor." Then I provided a library of ways
of getting called without blocking when you're busy, such as marshalling
up the arguments and queueing them, with a task that loops and dequeues
the arguments. In other words, I reimplemented Ada's entry queues.
 
> > > I haven't found a need for extensible tasks.
> >
> > I have, but then that's the kind of software I write.
> 
> To be precise, you have a need for dispatching in a tasking context,
> and tried a design approach that doesn't work in Ada. There are other
> approaches; if one of them works, you have not found a need for
> extensible tasks :).

Well, sure, I could write a Java JVM in Ada and then I'd not need any
extensions to Ada. That's not to say it's an appropriate solution.
Personally, I don't think reimplementing the entry concept in Ada source
code should be necessary.

-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
  The opposite of always is sometimes.
   The opposite of never is sometimes.



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-16 15:05       ` Dmitry A. Kazakov
@ 2002-01-16 18:30         ` Matthew Heaney
  2002-01-17  8:58           ` Dmitry A. Kazakov
  2002-01-16 20:28         ` Robert A Duff
  2002-01-17 19:05         ` Nick Roberts
  2 siblings, 1 reply; 41+ messages in thread
From: Matthew Heaney @ 2002-01-16 18:30 UTC (permalink / raw)



"Dmitry A. Kazakov" <dmitry@elros.cbb-automation.de> wrote in message
news:3c45865f.2709203@News.CIS.DFN.DE...
> However, I always wished Ada having an ability to disallow primitive
> operations. Like:
>
> type Unordered is new Integer;
> function ">" (Left, Right : Unordered) is abstract; -- Disallow ">"

I'm confused by your comment.  Ada95 *can* do this.

I do this to turn off predefined equality for a composite type whose
component type is a generic formal non-limited type, so that I don't
accidently use predefined array comparisons using the predefined equality
for the formal type, which reemerges in the composite type, ie

generic
   type T is private;
package GP is
   type T_Array is array (Positive range <>) of T;
   function "=" (L, R : T_Array) return Boolean is abstract;
end GP;

Note that passing in an equality operator doesn't work here, because
predefined equality reemerges *again* in the array declaration.  So I just
turn off array comparison, in order to prevent any accidents.


> >But the tagged type could contain a component of a task type.
>
> Yes, and another problem as well. Finalize is called *after* all task
> components has been terminated. So when the object is being destroyed
> you cannot notify tasks about that. You must use pointers to tasks if
> you need a "prepare-to-die" notification.

But you can use a two-part termination:

package P is
   type T (<>) is abstract tagged limited private;
   type T_Class_Access is access all T'Class;
   procedure Op (O : access T) is abstract;
   procedure Free (O : in out T_Class_Access);
private
   type T_Task_Type (O : access T'Class) is
      entry E;
   end T_Task_Type;

   type T is abstract new Limited_Controlled with record
      T_Task : T_Task_Type (T'Access);
   end record;

   procedure Do_Free (O : access T);  --private, primitive op
end P;


package body P is
   procedure Do_Free (O : access T) is
   begin
      null;
   end;

   procedure Free (O : in out T_Class_Access) is
      procedure Deallocate is new Ada.UD (T'Class, T_Class_Access);
   begin
      if O /= null then
         Do_Free (O);
         Deallocate (O);
      end if;
   end Free;
...
end P;

Each type in T'Class provides a factory function to construct instances.  To
destruct an instance, the client calls P.Free.  Free is implemented by
calling dispatching Do_Free, which notifies the object that it's about to be
destroyed.  It then calls deallocate, which triggers the Finalize call.

Won't this work?






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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-16 15:05       ` Dmitry A. Kazakov
  2002-01-16 18:30         ` Matthew Heaney
@ 2002-01-16 20:28         ` Robert A Duff
  2002-01-17 19:05         ` Nick Roberts
  2 siblings, 0 replies; 41+ messages in thread
From: Robert A Duff @ 2002-01-16 20:28 UTC (permalink / raw)


dmitry@elros.cbb-automation.de (Dmitry A. Kazakov) writes:

> However, I always wished Ada having an ability to disallow primitive
> operations. Like:
> 
> type Unordered is new Integer;
> function ">" (Left, Right : Unordered) is abstract; -- Disallow ">"

Umm, isn't that legal?  And does it not disallow calls to ">"?
Of course, it's a pain to list all the operations you *don't*
want -- I'd rather it be the other way 'round.

> The difference is that with a driver you always know the interface:
> Open/Read/Write/Close.

And ioctl, which is 37-to-50 different operations wrapped up in one,
different for each driver.  ;-)

- Bob



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-16  0:57           ` Darren New
  2002-01-16 16:35             ` Stephen Leake
@ 2002-01-16 23:04             ` Matthew Heaney
  2002-01-17  0:21               ` Darren New
  1 sibling, 1 reply; 41+ messages in thread
From: Matthew Heaney @ 2002-01-16 23:04 UTC (permalink / raw)



"Darren New" <dnew@san.rr.com> wrote in message
news:3C44CFBD.BC1ED52F@san.rr.com...
> Sure. Now Op can't act as an entry.

I don't understand this comment.  Op is a procedure, so by definition it
isn't an entry.


> The basic problem is that if I have something that says
>   x.boop
> and boop is an entry,

I don't understand this comment.  Is x a task object, protected object, or
something else?  How can boop be anything other than an entry?

> it can only call one specific piece of code. That
> one specific piece of code has to be the one that determines whether the
> code blocks.

Can you say Boop (X), and then implement operation Boop to have the behavior
you want?


> That's not guarding an entry. If T.op dispatches, then it can't be an
> entry. If it's an entry, I can't override it.

I don't understand this comment.  If you use the syntax
   T.op
then T must be a task (or protected object).  And in that case it's an
entry, and so of course it doesn't dispatch.

If you had said:  "If Op (T) dispatches" then I would understand your
question about whether it dispatches.  But you didn't, so I'm confused.

What's wrong with saying Op (T), and implementing Op as a primitive
operation of T?


> I can't do
>   select T.op or delay 10.0 ; yadda end select;
> and have T.op be one of two different pieces of code, for example.

This is a selective entry call.  So wrap it in a dispatching operation:

function Op (O : in out T; Timeout : Duration) return Boolean is
begin
   select
      O.T_Task.Op;
      return False;
   or
      delay Timeout;
      return True;
   end select;
end Op;

Now if you say:

   Timed_Out := Op (O, 10.0);

then you get blocking and dispatching.


> The problem I had was trying to build a library wherein the framework
> instantiates a number of tasks. Each task is provided by the user of the
> framework. (Think, for example, of someone writing multiple types of
> windows in a GUI framework.) Each task would need to respond to
> different events at different times (meaning selective accepts in the
> tasks)

Don't use selective accepts in tasks.  This is old-fashioned.  Rewrite your
tasks to make blocking calls to an internal protected object.  Have
operations communicate with the task by calling the protected object, which
wakes up the waiting task.









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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-16 18:07               ` Darren New
@ 2002-01-16 23:18                 ` Matthew Heaney
  0 siblings, 0 replies; 41+ messages in thread
From: Matthew Heaney @ 2002-01-16 23:18 UTC (permalink / raw)



"Darren New" <dnew@san.rr.com> wrote in message
news:3C45C12D.A229F569@san.rr.com...
> Errr, no. I'm pretty sure a selective accept works in tasks.

Yes, it does, but you shouldn't bother using it.  Have the task wait on a
guarded entry of a protected object, and communicate with the task by
calling a protected procedure of the protected object.


> Yes. But the problem is in the callee you want a selective accept, and
> in the caller you want a conditional entry, and you don't want those two
> tied together at compile time.

Fine.  So don't tie them together at compile time.  The task calls a
dispatching operation, and that dispatching operation is implemented in a
derived type by calling a guarded entry of a protected object.

The callee doesn't call an entry directly.  He calls a primitive operation
that is implemented by calling an entry (of a protected object).  I showed
this in my last post.


> In that case, you're screwed. If you
> could derive a task type from another task type, I'd be fine; that is,
> if I could have multiple different task bodies for the same task type,
> I'd not have had even conceptual problems.

The behavior that varies is encapsulated by calling private primitive
operations that dispatch:

package P is

   type T is abstract tagged limited private;

   procedure Op (O : in out T) is abstract;

private

   task type T_Task_Type (O : access T'Class);  --type is class-wide

   type T is abstract tagged limited record
      T_Task : T_Task_Type (T'Access);
   end record;

   procedure Wait (O : access T);  --primitive
end P;

package body P is

   procedure Wait (O : access T) is
   begin
      null;
      pragma Assert (False); --must override
   end;

   type T_Task_Type is
   begin
      loop
         Wait (O);  -- dispatches according to tag of discriminant
         ...
      end loop;
   end T_Task_Type;

end P;

Now you can implement a derived type by extending T with a protected object,
and then overidding Wait so that it calls an entry of the protected object.


> In other words, I reimplemented Ada's entry queues.

I don't think this is necessary.  Find a way to turn tasks into callers of
protected entries, so you can get rid of the select-accept statements.







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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-16 23:04             ` Matthew Heaney
@ 2002-01-17  0:21               ` Darren New
  0 siblings, 0 replies; 41+ messages in thread
From: Darren New @ 2002-01-17  0:21 UTC (permalink / raw)


Matthew Heaney wrote:
> Yes, it does, but you shouldn't bother using it.  Have the task wait on a
> guarded entry of a protected object, and communicate with the task by
> calling a protected procedure of the protected object.

I must admit that I'm not 100% clear on how this is better than a
selective accept, given that a selective accept allows me to time out
and to pick whichever entry is available first. I don't think I can pick
whichever protected object guard opens first, can I?

> Don't use selective accepts in tasks.  This is old-fashioned.  Rewrite your
> tasks to make blocking calls to an internal protected object.  Have
> operations communicate with the task by calling the protected object, which
> wakes up the waiting task.

Which is basically what I did. Except there were about 20 or 30 entry
points, with various combinations or acceptabilities - i.e., only some
operations made sense in some states. There was also a requirement to
wait for events from other sources (e.g., events from the UI or events
from the I/O framework). So I wound up with protected entries that
waited for this or that or the other, then told you which, which you
then called.... anyway, it was an awful mess compared to what it could
have been had I been able to have multiple task bodies defined for a
single task type.

But thanks for the advice. If I ever go back to developing this, I'll
rethink it again.

-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
  The opposite of always is sometimes.
   The opposite of never is sometimes.



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-16 18:30         ` Matthew Heaney
@ 2002-01-17  8:58           ` Dmitry A. Kazakov
  2002-01-17  9:19             ` Lutz Donnerhacke
  0 siblings, 1 reply; 41+ messages in thread
From: Dmitry A. Kazakov @ 2002-01-17  8:58 UTC (permalink / raw)


On Wed, 16 Jan 2002 13:30:50 -0500, "Matthew Heaney" <mheaney@on2.com>
wrote:

>"Dmitry A. Kazakov" <dmitry@elros.cbb-automation.de> wrote in message
>news:3c45865f.2709203@News.CIS.DFN.DE...
>> However, I always wished Ada having an ability to disallow primitive
>> operations. Like:
>>
>> type Unordered is new Integer;
>> function ">" (Left, Right : Unordered) is abstract; -- Disallow ">"
>
>I'm confused by your comment.  Ada95 *can* do this.
>
>I do this to turn off predefined equality for a composite type whose
>component type is a generic formal non-limited type, so that I don't
>accidently use predefined array comparisons using the predefined equality
>for the formal type, which reemerges in the composite type, ie
>
>generic
>   type T is private;
>package GP is
>   type T_Array is array (Positive range <>) of T;
>   function "=" (L, R : T_Array) return Boolean is abstract;
>end GP;
>
>Note that passing in an equality operator doesn't work here, because
>predefined equality reemerges *again* in the array declaration.  So I just
>turn off array comparison, in order to prevent any accidents.

Yes it works (I have actually used that) for some cases. It does not
work when the type is non-abstract and tagged.

Sorry for my misleading comment.

I believe it is worth to allow that for all types, i.e. to allow
overriding a non-abstract subroutine with an abstract one. A
dispatching call to a disallowed subprogram will then raise an
exception [like when tags of arguments are different].

>> >But the tagged type could contain a component of a task type.
>>
>> Yes, and another problem as well. Finalize is called *after* all task
>> components has been terminated. So when the object is being destroyed
>> you cannot notify tasks about that. You must use pointers to tasks if
>> you need a "prepare-to-die" notification.
>
>But you can use a two-part termination:
>
>package P is
>   type T (<>) is abstract tagged limited private;
>   type T_Class_Access is access all T'Class;
>   procedure Op (O : access T) is abstract;
>   procedure Free (O : in out T_Class_Access);
>private
>   type T_Task_Type (O : access T'Class) is
>      entry E;
>   end T_Task_Type;
>
>   type T is abstract new Limited_Controlled with record
>      T_Task : T_Task_Type (T'Access);
>   end record;
>
>   procedure Do_Free (O : access T);  --private, primitive op
>end P;
>
>
>package body P is
>   procedure Do_Free (O : access T) is
>   begin
>      null;
>   end;
>
>   procedure Free (O : in out T_Class_Access) is
>      procedure Deallocate is new Ada.UD (T'Class, T_Class_Access);
>   begin
>      if O /= null then
>         Do_Free (O);
>         Deallocate (O);
>      end if;
>   end Free;
>...
>end P;
>
>Each type in T'Class provides a factory function to construct instances.  To
>destruct an instance, the client calls P.Free.  Free is implemented by
>calling dispatching Do_Free, which notifies the object that it's about to be
>destroyed.  It then calls deallocate, which triggers the Finalize call.
>
>Won't this work?

Surely. But I prefer my solution because it hides nasty pointers in
the implementation. Your variant exposes them to the user. Using C++
for a long time one becomes pointer-allergic. (:-)) In fact it is the
*same* solution, i.e. pointers are inevitable. Would not it be better
to add to Ada.Finalization

procedure Postinitialize (..); -- All tasks are already running
procedure Prefinalize (..);    -- All tasks are still running

?

Regards,
Dmitry Kazakov



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-17  8:58           ` Dmitry A. Kazakov
@ 2002-01-17  9:19             ` Lutz Donnerhacke
  2002-01-17 10:42               ` Dmitry A. Kazakov
  0 siblings, 1 reply; 41+ messages in thread
From: Lutz Donnerhacke @ 2002-01-17  9:19 UTC (permalink / raw)


* Dmitry A. Kazakov wrote:
>I believe it is worth to allow that for all types, i.e. to allow
>overriding a non-abstract subroutine with an abstract one. A
>dispatching call to a disallowed subprogram will then raise an
>exception [like when tags of arguments are different].

I strongly discourage from such a change. Class wide programming using
abstract tagged types are used for the guarantee of a working interface.




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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-17  9:19             ` Lutz Donnerhacke
@ 2002-01-17 10:42               ` Dmitry A. Kazakov
  2002-01-17 10:55                 ` Lutz Donnerhacke
  0 siblings, 1 reply; 41+ messages in thread
From: Dmitry A. Kazakov @ 2002-01-17 10:42 UTC (permalink / raw)


On Thu, 17 Jan 2002 09:19:59 +0000 (UTC), lutz@iks-jena.de (Lutz
Donnerhacke) wrote:

>* Dmitry A. Kazakov wrote:
>>I believe it is worth to allow that for all types, i.e. to allow
>>overriding a non-abstract subroutine with an abstract one. A
>>dispatching call to a disallowed subprogram will then raise an
>>exception [like when tags of arguments are different].
>
>I strongly discourage from such a change. Class wide programming using
>abstract tagged types are used for the guarantee of a working interface.

There is no way to enforce LSP, if you aim at that. Just consider an
overriding like:

procedure Do_Something (...) is
begin
   raise Program_Error;
end Do_Something;

And who said that derived types shall be LSP subtypes? What should we
do with

subtype Positive ...;

then?

Regards,
Dmitry Kazakov



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-17 10:42               ` Dmitry A. Kazakov
@ 2002-01-17 10:55                 ` Lutz Donnerhacke
  2002-01-17 15:30                   ` Dmitry A. Kazakov
  0 siblings, 1 reply; 41+ messages in thread
From: Lutz Donnerhacke @ 2002-01-17 10:55 UTC (permalink / raw)


* Dmitry A. Kazakov wrote:
>* lutz@iks-jena.de (Lutz Donnerhacke) wrote:
>>* Dmitry A. Kazakov wrote:
>>>I believe it is worth to allow that for all types, i.e. to allow
>>>overriding a non-abstract subroutine with an abstract one. A
>>>dispatching call to a disallowed subprogram will then raise an
>>>exception [like when tags of arguments are different].
>>
>>I strongly discourage from such a change. Class wide programming using
>>abstract tagged types are used for the guarantee of a working interface.
>
>There is no way to enforce LSP, if you aim at that. Just consider an
>overriding like:
>
>procedure Do_Something (...) is
>begin
>   raise Program_Error;
>end Do_Something;

Then the reason is on client site. Which is easy to detect.

>And who said that derived types shall be LSP subtypes?

The generic preamble requiring the correct instantiation.



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-17 10:55                 ` Lutz Donnerhacke
@ 2002-01-17 15:30                   ` Dmitry A. Kazakov
  2002-01-17 16:29                     ` Lutz Donnerhacke
  0 siblings, 1 reply; 41+ messages in thread
From: Dmitry A. Kazakov @ 2002-01-17 15:30 UTC (permalink / raw)


On Thu, 17 Jan 2002 10:55:57 +0000 (UTC), lutz@iks-jena.de (Lutz
Donnerhacke) wrote:

>* Dmitry A. Kazakov wrote:
>>* lutz@iks-jena.de (Lutz Donnerhacke) wrote:
>>>* Dmitry A. Kazakov wrote:
>>>>I believe it is worth to allow that for all types, i.e. to allow
>>>>overriding a non-abstract subroutine with an abstract one. A
>>>>dispatching call to a disallowed subprogram will then raise an
>>>>exception [like when tags of arguments are different].
>>>
>>>I strongly discourage from such a change. Class wide programming using
>>>abstract tagged types are used for the guarantee of a working interface.
>>
>>There is no way to enforce LSP, if you aim at that. Just consider an
>>overriding like:
>>
>>procedure Do_Something (...) is
>>begin
>>   raise Program_Error;
>>end Do_Something;
>
>Then the reason is on client site. Which is easy to detect.

How it differentiates from:

procedure Do_Something (..) is abstract;

The later has an additional advantage that some calls to it can be
detected at compile time.

Consider disallowing methods as an additional constraint. Any
constraint imposed by a derived type is incompatible with LSP. Should
we then dismiss the idiom of constrained subtypes?

>>And who said that derived types shall be LSP subtypes?
>
>The generic preamble requiring the correct instantiation.

You must define the word correct. If correct = absolutely
substitutable then of course LSP is required.

Regards,
Dmitry Kazakov



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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-17 15:30                   ` Dmitry A. Kazakov
@ 2002-01-17 16:29                     ` Lutz Donnerhacke
  0 siblings, 0 replies; 41+ messages in thread
From: Lutz Donnerhacke @ 2002-01-17 16:29 UTC (permalink / raw)


* Dmitry A. Kazakov wrote:
>How it differentiates from:
>procedure Do_Something (..) is abstract;
>
>The later has an additional advantage that some calls to it can be
>detected at compile time.

You are right. I'm still feeling bad on this issue.




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

* Re: How to avoid unreferenced objects (mutexes etc)
  2002-01-16 15:05       ` Dmitry A. Kazakov
  2002-01-16 18:30         ` Matthew Heaney
  2002-01-16 20:28         ` Robert A Duff
@ 2002-01-17 19:05         ` Nick Roberts
  2 siblings, 0 replies; 41+ messages in thread
From: Nick Roberts @ 2002-01-17 19:05 UTC (permalink / raw)


"Dmitry A. Kazakov" <dmitry@elros.cbb-automation.de> wrote in message
news:3c45865f.2709203@News.CIS.DFN.DE...

> Well, you can derive from the base in the private part and expose
> operations you want to keep visible using wrappers implemented via
> renaming in the body.

This suggests a less than ideal design, under normal circumstances (I gather
yours are not).

It sounds like each of your steps, from the lowest levels to the highest, is
too small. Each step should, ideally, be a significant conceptual 'jump'. I
do know that there exceptions to this idea (I have dealt with a few), but it
is a good general rule.

> However, I always wished Ada having an ability to disallow primitive
> operations. Like:
>
> type Unordered is new Integer;
> function ">" (Left, Right : Unordered) is abstract; -- Disallow ">"

... an advantage, of course, of using a completely new type (rather than a
derived one).

> Yes, and another problem as well. Finalize is called *after* all task
> components has been terminated. So when the object is being destroyed
> you cannot notify tasks about that. You must use pointers to tasks if
> you need a "prepare-to-die" notification.

I have never actually considered this problem, in practice, for the simple
reason that I have always coded explicit Startup, Reset, and Shutdown
operations into the interface, at every level. I simply don't - in my own
mind - include this functionality in the duties of Finalize (but that's just
my opinion, of course). Of course, my designs all stem from before 1995.

> >> An untagged solution requires an increasing number of
> >> wrapping subroutines [thus run-time penalty].
> >
> >I really don't understand why there should be a greater time penalty for
> >this solution. I think possibly this is a misunderstanding that lies at
the
> >heart of your problems.
>
> Maybe the penalty is not very high, but writting dozens of wrappers is
> disgusting.

I suggest you must 'bite the bullet' of re-implementing operations carried
up from one level to the next. Each will tend to be of the form:

   procedure Flash_Big_Yellow_Light (Device: access Medium_Level_Device) is
   begin
      Flash_Big_Yellow_Light(Device.Low_Level_Part);
   end;

This (3 lines) is instead of:

   procedure Flash_Big_Yellow_Light (Device: access Medium_Level_Device)
      renames Low_Level.Lights.Yellow.Flash;

(1 or 2 lines), or inheritance (0 lines). I'm saying that, although 3 or 4
lines per subprogram is a bit of a pain (and I'm familiar with interfaces
that have hundreds of operations), it's usually worth the effort in terms of
getting a clean separation between the levels.

This 'clean separation' typically pays dividends evetually, in terms of
making things easier for the client (application) software, easing
maintenance of the code at each level, and especially easing the exchanging
of one implementation at a certain level with another (something important
to you, do I gather?).

From an efficiency point of view, heavy use of Inline is sometimes feasible.

> >I don't really see generics being directly relevant to the problem here.
>
> You can make package implementing some interface layer generic with a
> lower level package as the formal parameter.

Yes, but how is this of significance to synchronisation mechanisms? Anyway,
it does sound like you need a dynamic way of switching implementations
(either by dispatching or using access-to-subprogram types).

> [ driver example snipped ]
>
> The difference is that with a driver you always know the interface:
> Open/Read/Write/Close. The protocols we must implement are more
> complex. Additionally one should have an ability to replace any part
> of the hierarchy. For instance, low level: transport [like sockets vs.
> RS323], middle level: time synchronization protocol, high level client
> vs. server. Of course this ideal is reachless, so I am considering a
> which "local optimum" to take. (:-))

Yes, that does sound like a very unusual requirement. Best of luck!

--
Best wishes,
Nick Roberts






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

end of thread, other threads:[~2002-01-17 19:05 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-01-11 13:48 How to avoid unreferenced objects (mutexes etc) Dmitry A. Kazakov
2002-01-11 13:52 ` Lutz Donnerhacke
2002-01-11 14:47 ` Robert A Duff
2002-01-11 18:02 ` Jeffrey Carter
2002-01-11 19:40 ` Robert Dewar
2002-01-12 10:18   ` Martin Dowie
2002-01-14  8:54   ` Dmitry A. Kazakov
2002-01-12  1:11 ` Nick Roberts
2002-01-12 22:04   ` Matthew Heaney
2002-01-13  5:45     ` Nick Roberts
2002-01-13  8:21       ` tmoran
2002-01-13 16:12         ` Nick Roberts
2002-01-13 15:08       ` Simon Wright
2002-01-15 17:53         ` Nick Roberts
2002-01-13 16:51       ` Jeffrey Carter
2002-01-14 23:32       ` Matthew Heaney
2002-01-15  8:53         ` Dmitry A. Kazakov
2002-01-14  8:31     ` Jean-Pierre Rosen
2002-01-14  9:42   ` Dmitry A. Kazakov
2002-01-15 15:41     ` Matthew Heaney
2002-01-15 16:18       ` Hyman Rosen
2002-01-15 16:57       ` Darren New
2002-01-15 18:57         ` Matthew Heaney
2002-01-16  0:57           ` Darren New
2002-01-16 16:35             ` Stephen Leake
2002-01-16 18:07               ` Darren New
2002-01-16 23:18                 ` Matthew Heaney
2002-01-16 23:04             ` Matthew Heaney
2002-01-17  0:21               ` Darren New
2002-01-16 15:18       ` Dmitry A. Kazakov
2002-01-15 18:59     ` Nick Roberts
2002-01-16 15:05       ` Dmitry A. Kazakov
2002-01-16 18:30         ` Matthew Heaney
2002-01-17  8:58           ` Dmitry A. Kazakov
2002-01-17  9:19             ` Lutz Donnerhacke
2002-01-17 10:42               ` Dmitry A. Kazakov
2002-01-17 10:55                 ` Lutz Donnerhacke
2002-01-17 15:30                   ` Dmitry A. Kazakov
2002-01-17 16:29                     ` Lutz Donnerhacke
2002-01-16 20:28         ` Robert A Duff
2002-01-17 19:05         ` Nick Roberts

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