comp.lang.ada
 help / color / mirror / Atom feed
* Task components, the rationale
@ 2011-07-13 18:52 Dmitry A. Kazakov
  2011-07-13 20:58 ` Maciej Sobczak
  0 siblings, 1 reply; 6+ messages in thread
From: Dmitry A. Kazakov @ 2011-07-13 18:52 UTC (permalink / raw)


OK, to avoid false impression that Ada was carelessly designed, it must be
said that there was a valid reason why task components are broken this way.
That is to prevent a much worse disaster when so-called Rosen's trick is
used.

Consider the pattern discussed earlier (the Rosen's trick):

   type T;
   task type Worker (Self : not null access T'Class);
   type T is new Ada.Finalization.Limited_Controlled with record
      My_Worker : Worker (T'Access);
   end record;
   overriding procedure Initialize (Object : in out T);
   procedure Foo (Object : in out T) is abstract; -- A primitive operation
   
Now, if My_Worker started before completion of Initialize then this body

   task body Worker is
   begin
       Self.Foo; -- Boom!

could call Foo of T or any of its derived type *before* Initialize, i.e.
before the object's construction is done! That would be a much worse
problem.

There is no simple solution for this. To start with tasks must be
inheritable from and their bodies must be primitive or class-wide
operations, because aggregation (composition) + Rosen's trick is
necessarily broken.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Task components, the rationale
  2011-07-13 18:52 Task components, the rationale Dmitry A. Kazakov
@ 2011-07-13 20:58 ` Maciej Sobczak
  2011-07-14  8:52   ` Georg Bauhaus
  2011-07-14  9:23   ` Dmitry A. Kazakov
  0 siblings, 2 replies; 6+ messages in thread
From: Maciej Sobczak @ 2011-07-13 20:58 UTC (permalink / raw)


On Jul 13, 8:52 pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
wrote:

> Now, if My_Worker started before completion of Initialize then this body
>
>    task body Worker is
>    begin
>        Self.Foo; -- Boom!
>
> could call Foo of T or any of its derived type *before* Initialize, i.e.
> before the object's construction is done! That would be a much worse
> problem.

I don't even think you need to introduce tasks to show the problem -
what if the component is of another controlled type? Then you have two
nested calls to distinct Initialize operations - the first one for the
component (where you have the discriminant access value to play with)
and the second one for the whole, which is too late:

with Ada.Finalization;
with Ada.Text_IO;

procedure Test is

   type Outer;

   type Inner (Shell : access Outer) is
     new Ada.Finalization.Limited_Controlled with null record;

   overriding procedure Initialize (Self : in out Inner);

   type Outer is new Ada.Finalization.Limited_Controlled with record
      I : Inner (Outer'Access);
      Some_Value : Integer;
   end record;

   overriding procedure Initialize (Self : in out Outer);

   procedure Initialize (Self : in out Inner) is
   begin
      Ada.Text_IO.Put_Line
        ("initializing inner, Self.Shell.Some_Value =" &
           Integer'Image (Self.Shell.all.Some_Value));
   end Initialize;

   procedure Initialize (Self : in out Outer) is
   begin
      Self.Some_Value := 123;
      Ada.Text_IO.Put_Line
        ("initialized outer, Some_Value =" &
           Integer'Image (Self.Some_Value));
   end Initialize;

   X : Outer;

begin
   null;
end Test;

$ gnatmake test
...
$ ./test
initializing inner, Self.Shell.Some_Value = 0
initialized outer, Some_Value = 123
$

We are messing with the state that does not yet exist. Oops.

> There is no simple solution for this.

You have to just, you know, simply, introduce constructors to the
language. This is my pet feature for Ada 2020. :-)

> To start with tasks must be
> inheritable from and their bodies must be primitive or class-wide
> operations, because aggregation (composition) + Rosen's trick is
> necessarily broken.

It's not about tasks, it's about access discriminants to outer records
- they introduce circular references (outer has inner, inner knows
outer) and as such are evil.

--
Maciej Sobczak * http://www.msobczak.com * http://www.inspirel.com



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

* Re: Task components, the rationale
  2011-07-13 20:58 ` Maciej Sobczak
@ 2011-07-14  8:52   ` Georg Bauhaus
  2011-07-14 18:15     ` Maciej Sobczak
  2011-07-14  9:23   ` Dmitry A. Kazakov
  1 sibling, 1 reply; 6+ messages in thread
From: Georg Bauhaus @ 2011-07-14  8:52 UTC (permalink / raw)


On 7/13/11 10:58 PM, Maciej Sobczak wrote:
> On Jul 13, 8:52 pm, "Dmitry A. Kazakov"<mail...@dmitry-kazakov.de>

>> There is no simple solution for this.
>
> You have to just, you know, simply, introduce constructors to the
> language. This is my pet feature for Ada 2020. :-)

Out of curiosity, would this be enough?  How will it work?
Assuming, naively, not knowing C++, that constructors of C++
could lead the way,   I get

#include <iostream>

namespace
{
   class Outer;

   class Inner {
   private:
     Outer* shell;
   public:
     Inner(Outer*);
   };

   class Outer {
   private:
     Inner i;
   public:
     int some_value;
     Outer();
   };

   Inner::Inner(Outer* wrap) {
     this->shell = wrap;
     std::cout << "initializing inner, this->shell->some_value = "
               << this->shell->some_value << std::endl;
   }

   Outer::Outer() : i(this) {
     this->some_value = 123;
     std::cout << "initialized outer, this->some_value = "
               << this->some_value << std::endl;
   }
}

int main()
{
   Outer x;

   return 0;
}


$ c++ news23.cpp
$ ./a.out
initializing inner, this->shell->some_value = 1606422610
initialized outer, this->some_value = 123
$



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

* Re: Task components, the rationale
  2011-07-13 20:58 ` Maciej Sobczak
  2011-07-14  8:52   ` Georg Bauhaus
@ 2011-07-14  9:23   ` Dmitry A. Kazakov
  1 sibling, 0 replies; 6+ messages in thread
From: Dmitry A. Kazakov @ 2011-07-14  9:23 UTC (permalink / raw)


On Wed, 13 Jul 2011 13:58:36 -0700 (PDT), Maciej Sobczak wrote:

> On Jul 13, 8:52�pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
> wrote:
> 
>> Now, if My_Worker started before completion of Initialize then this body
>>
>> � �task body Worker is
>> � �begin
>> � � � �Self.Foo; -- Boom!
>>
>> could call Foo of T or any of its derived type *before* Initialize, i.e.
>> before the object's construction is done! That would be a much worse
>> problem.
> 
> I don't even think you need to introduce tasks to show the problem -
> what if the component is of another controlled type?

Yes, but the hack was made specifically for tasks.

If anybody wished to have safe construction/destruction in presence of
components spoiled by the Rosen's trick, he would need to postpone parts of
constructors/destructors to arrange them in certain order. It guaranteed is
impossible to do in certain cases. With task components that manifests
itself as a deadlock. (I don't know if the problem is detectable through
static analysis, but I doubt it is.)

>> There is no simple solution for this.
> 
> You have to just, you know, simply, introduce constructors to the
> language. This is my pet feature for Ada 2020. :-)

Well, constructors need to be properly crafted to handle this. Note that
the problem is in inconsistencies at the typing level. Returning to the
tasks, you have to properly attribute the task body. Is it a primitive
operation? Is it class-wide? etc. Depending on that you should be able or
not to dispatch from the body and that will determine the earliest stage of
construction when the body is allowed to start and the latest point before
it started. I think it would not be possible to do without class-wide
constructors, e.g. ones constructing classes out of specific types. (This
is a subproblem of a more general problem: dispatching upon
construction/destruction.)
 
>> To start with tasks must be
>> inheritable from and their bodies must be primitive or class-wide
>> operations, because aggregation (composition) + Rosen's trick is
>> necessarily broken.
> 
> It's not about tasks, it's about access discriminants to outer records
> - they introduce circular references (outer has inner, inner knows
> outer) and as such are evil.

Yes.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Task components, the rationale
  2011-07-14  8:52   ` Georg Bauhaus
@ 2011-07-14 18:15     ` Maciej Sobczak
  2011-07-22 23:28       ` Randy Brukardt
  0 siblings, 1 reply; 6+ messages in thread
From: Maciej Sobczak @ 2011-07-14 18:15 UTC (permalink / raw)


On Jul 14, 10:52 am, Georg Bauhaus <rm.dash-bauh...@futureapps.de>
wrote:

> > You have to just, you know, simply, introduce constructors to the
> > language. This is my pet feature for Ada 2020. :-)
>
> Out of curiosity, would this be enough?

It would not be sufficient, but it would be necessary.

The point is, in order to solve these kind of puzzles you have to
recognize initialization as a special operation (ie. stop pretending
that it can be a regular primitive operation of a type) and use that
notion to impose special rules.

In the case of access discriminants the circular relationship is
possible to discover statically. After all, the whole T'Access
"expression" is special, and allowed only in this particular case.
Once you statically know you have a problem, you can work from there -
but no matter what kind of restrictions or provisions you impose in
the constructor, you have to recognize that it is a special operation,
not a regular primitive one.

If you ask me from the top of my head how *exactly* this can be
solved, I will not attempt to give a full solution (hey, the committee
has a full decade for it ;-) ), but one of the possible ideas might
involve adding a lifetime information to the access discriminant, just
as it is done for tracking scopes of types and objects with anonymous
access parameters today. That is, raise Program_Error when you
discover that within the constructor of T its access discriminant
(pointer to outer) is dereferenced while the outer was not yet
initialized.

Most cases (like the two examples we have shown) can be fully analyzed
statically for this.

> Assuming, naively, not knowing C++, that constructors of C++
> could lead the way,

They will not lead the way in solving the problem of dangling
pointers, because this is not the problem that C++ was designed to
solve in general. But recognizing that the constructor is a special
place is an important contribution.

--
Maciej Sobczak * http://www.msobczak.com * http://www.inspirel.com



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

* Re: Task components, the rationale
  2011-07-14 18:15     ` Maciej Sobczak
@ 2011-07-22 23:28       ` Randy Brukardt
  0 siblings, 0 replies; 6+ messages in thread
From: Randy Brukardt @ 2011-07-22 23:28 UTC (permalink / raw)


"Maciej Sobczak" <see.my.homepage@gmail.com> wrote in message 
news:a30f4a5d-baee-40b5-b30d-2a15042868e7@r9g2000yql.googlegroups.com...
...
>If you ask me from the top of my head how *exactly* this can be
>solved, I will not attempt to give a full solution (hey, the committee
>has a full decade for it ;-) ),

Well, actually, work on Ada 2020 would need to be finished by late 2018 in 
order to have a good chance of being standardized in 2020. Since it is 
mid-2011 now, I think that is more like 7 years than 10.

...
> but one of the possible ideas might
>involve adding a lifetime information to the access discriminant, just
>as it is done for tracking scopes of types and objects with anonymous
>access parameters today.

That was suggested for Ada 2012 [by me and others], and it turns out that it 
cannot be done (at least with the sorts of lifetime indications that Ada has 
used to date). If it was mandated, it would necessarily make Ada 
implementations far more expensive than they currently are -- so I doubt 
very much that we'll see that. (Sorry, I don't remember which AI we were 
discussing at the time, so I can't give you a reference.)

The static accessibility model for access discriminants is *very* 
problematical; it leads to distributed overhead for functions that might 
return something with a discriminant -- yet that still is considered 
preferable to any dynamic model.  My preference is to not use them at all 
(not always possible, as shown by some of these examples).

                                      Randy.


                              Randy.





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

end of thread, other threads:[~2011-07-22 23:28 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-13 18:52 Task components, the rationale Dmitry A. Kazakov
2011-07-13 20:58 ` Maciej Sobczak
2011-07-14  8:52   ` Georg Bauhaus
2011-07-14 18:15     ` Maciej Sobczak
2011-07-22 23:28       ` Randy Brukardt
2011-07-14  9:23   ` Dmitry A. Kazakov

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