comp.lang.ada
 help / color / mirror / Atom feed
* Finalization of a record containing a task
@ 2005-02-16 20:35 Bj?rn
  2005-02-16 20:49 ` Stephen Leake
  0 siblings, 1 reply; 15+ messages in thread
From: Bj?rn @ 2005-02-16 20:35 UTC (permalink / raw)


I'm trying to implement a form of self modifying ADT, containing an
active task.

I would prefer that the user of the ADT not need to do any "extra"
operations on the ADT when the user is finished with it, rather have
the task terminate automatically when the type goes out of scope.

I do not see how I can manage this since the ADT does not seem to
finalize as long as the tasks have not terminated.

How can I solve this? Should I rather try a different approach?

Currently it looks something like this:

package PKG is
   type T_ADT is new Limited_Controlled with private
   ...
private
   task type T_Worker is
      entry Start;
      entry Stop;
      ...
   end task;
   
   procedure Initialize (T : in out T_ADT);
   procedure Finalize (T : in out T_ADT);

   type T_ADT is new Limited_Controlled with
      record
         X : Some_Type;
         Worker : T_Worker;
      end record;
end PKG;

package body PKG is
   task T_Worker is
      {Inner task of T_Worker}
   begin
      loop
         select
            accept Start;
         or
            accept Stop;
         or
            ...
         or
            delay until Some_Time;
         end select;
      end loop;
end PKG;

Best regards,
Bj�rn



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

* Re: Finalization of a record containing a task
  2005-02-16 20:35 Finalization of a record containing a task Bj?rn
@ 2005-02-16 20:49 ` Stephen Leake
  2005-02-16 21:20   ` Adrien Plisson
                     ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Stephen Leake @ 2005-02-16 20:49 UTC (permalink / raw)
  To: comp.lang.ada

ssh9614@hotmail.com (Bj?rn) writes:

> I'm trying to implement a form of self modifying ADT, containing an
> active task.
> 
> I would prefer that the user of the ADT not need to do any "extra"
> operations on the ADT when the user is finished with it, rather have
> the task terminate automatically when the type goes out of scope.
> 
> I do not see how I can manage this since the ADT does not seem to
> finalize as long as the tasks have not terminated.

you need to have a 'select' statement, with an open 'terminate'
alternative.

> package body PKG is
>    task T_Worker is
>       {Inner task of T_Worker}
>    begin
>       loop
>          select
>             accept Start;
>          or
>             accept Stop;
>          or
>             ...
>          or
>             delay until Some_Time;

           or terminate;

>          end select;
>       end loop;
> end PKG;

-- 
-- Stephe




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

* Re: Finalization of a record containing a task
  2005-02-16 20:49 ` Stephen Leake
@ 2005-02-16 21:20   ` Adrien Plisson
  2005-02-16 22:09     ` Robert A Duff
  2005-02-17  9:13     ` Dmitry A. Kazakov
  2005-02-16 22:08   ` Robert A Duff
  2005-02-17 17:50   ` Bj?rn
  2 siblings, 2 replies; 15+ messages in thread
From: Adrien Plisson @ 2005-02-16 21:20 UTC (permalink / raw)


Stephen Leake wrote:

> ssh9614@hotmail.com (Bj?rn) writes:
>>I do not see how I can manage this since the ADT does not seem to
>>finalize as long as the tasks have not terminated.
> 
> you need to have a 'select' statement, with an open 'terminate'
> alternative.

alternately, since T_ADT is Limited_Controlled, you may abort the inner 
task in its Finalize (!! baaad !!)

-- 
rien




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

* Re: Finalization of a record containing a task
  2005-02-16 20:49 ` Stephen Leake
  2005-02-16 21:20   ` Adrien Plisson
@ 2005-02-16 22:08   ` Robert A Duff
  2005-02-21 13:20     ` Craig Carey
  2005-02-17 17:50   ` Bj?rn
  2 siblings, 1 reply; 15+ messages in thread
From: Robert A Duff @ 2005-02-16 22:08 UTC (permalink / raw)


Stephen Leake <stephen_leake@acm.org> writes:

> you need to have a 'select' statement, with an open 'terminate'
> alternative.

That works in some cases, but you can't mix delay alternatives with
terminate alternatives.  Maybe you could replace the delay with an
accept, and arrange for some other task to call that entry
periodically.  But then how do you get rid of that *other* task?

You also can't mix entry calls with terminate alternatives, so if the
task wants to talk to a protected object, you're stuck.

> > package body PKG is
> >    task T_Worker is
> >       {Inner task of T_Worker}
> >    begin
> >       loop
> >          select
> >             accept Start;
> >          or
> >             accept Stop;
> >          or
> >             ...
> >          or
> >             delay until Some_Time;
> 
>            or terminate;
> 
> >          end select;
> >       end loop;
> > end PKG;
> 
> -- 
> -- Stephe

- Bob



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

* Re: Finalization of a record containing a task
  2005-02-16 21:20   ` Adrien Plisson
@ 2005-02-16 22:09     ` Robert A Duff
  2005-02-17  8:24       ` Adrien Plisson
  2005-02-17  9:13     ` Dmitry A. Kazakov
  1 sibling, 1 reply; 15+ messages in thread
From: Robert A Duff @ 2005-02-16 22:09 UTC (permalink / raw)


Adrien Plisson <aplisson-news@stochastique.net> writes:

> Stephen Leake wrote:
> 
> > ssh9614@hotmail.com (Bj?rn) writes:
> >>I do not see how I can manage this since the ADT does not seem to
> >>finalize as long as the tasks have not terminated.
> > you need to have a 'select' statement, with an open 'terminate'
> > alternative.
> 
> alternately, since T_ADT is Limited_Controlled, you may abort the inner
> task in its Finalize (!! baaad !!)

But the problem is that the program hangs, waiting for termination of
the task, *before* it calls the Finalize that does the abort
(or calls the Stop entry, in the original example).

- Bob



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

* Re: Finalization of a record containing a task
  2005-02-16 22:09     ` Robert A Duff
@ 2005-02-17  8:24       ` Adrien Plisson
  2005-02-18  0:17         ` Robert A Duff
  0 siblings, 1 reply; 15+ messages in thread
From: Adrien Plisson @ 2005-02-17  8:24 UTC (permalink / raw)


Robert A Duff wrote:
> Adrien Plisson <aplisson-news@stochastique.net> writes:
>>alternately, since T_ADT is Limited_Controlled, you may abort the inner
>>task in its Finalize (!! baaad !!)
> 
> 
> But the problem is that the program hangs, waiting for termination of
> the task, *before* it calls the Finalize that does the abort
> (or calls the Stop entry, in the original example).

euh... yes, you are right. i do remember now that when i did that, i 
was using an access to the task type.

anyway, it is a very bad idea since aborting the task may have a side 
effect: you don't know what was going on at the time you abort the 
task, so the state of the data accessed by the task is undefined.

-- 
rien



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

* Re: Finalization of a record containing a task
  2005-02-16 21:20   ` Adrien Plisson
  2005-02-16 22:09     ` Robert A Duff
@ 2005-02-17  9:13     ` Dmitry A. Kazakov
  2005-02-18  0:13       ` Robert A Duff
  1 sibling, 1 reply; 15+ messages in thread
From: Dmitry A. Kazakov @ 2005-02-17  9:13 UTC (permalink / raw)


On Wed, 16 Feb 2005 22:20:27 +0100, Adrien Plisson wrote:

> Stephen Leake wrote:
> 
>> ssh9614@hotmail.com (Bj?rn) writes:
>>>I do not see how I can manage this since the ADT does not seem to
>>>finalize as long as the tasks have not terminated.
>> 
>> you need to have a 'select' statement, with an open 'terminate'
>> alternative.
> 
> alternately, since T_ADT is Limited_Controlled, you may abort the inner 
> task in its Finalize (!! baaad !!)

No, there is no way to gracefully complete task components of upon
finalization of the controlled object. The finalization model does not
allow this, see Robert Duff's post. (Hypothetically, if there were
class-wide destructors, one would be able to do that.) So in most cases one
is forced to use access-to-task components rather than plain task ones.

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



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

* Re: Finalization of a record containing a task
  2005-02-16 20:49 ` Stephen Leake
  2005-02-16 21:20   ` Adrien Plisson
  2005-02-16 22:08   ` Robert A Duff
@ 2005-02-17 17:50   ` Bj?rn
  2 siblings, 0 replies; 15+ messages in thread
From: Bj?rn @ 2005-02-17 17:50 UTC (permalink / raw)


Ok. I will try doing this in some other way then.
Thank you all for clarification and suggestions!

Best regards,
Bj�rn



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

* Re: Finalization of a record containing a task
  2005-02-17  9:13     ` Dmitry A. Kazakov
@ 2005-02-18  0:13       ` Robert A Duff
  2005-02-18  2:34         ` Randy Brukardt
  2005-02-18 11:27         ` Dmitry A. Kazakov
  0 siblings, 2 replies; 15+ messages in thread
From: Robert A Duff @ 2005-02-18  0:13 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:

>... (Hypothetically, if there were
> class-wide destructors, one would be able to do that.)

Sounds interesting.  Can you give details of what you mean?

>... So in most cases one
> is forced to use access-to-task components rather than plain task ones.

Yes, but if you declare the object in a library package, you'll
still have the same problem with finalization happening too late.

- Bob



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

* Re: Finalization of a record containing a task
  2005-02-17  8:24       ` Adrien Plisson
@ 2005-02-18  0:17         ` Robert A Duff
  0 siblings, 0 replies; 15+ messages in thread
From: Robert A Duff @ 2005-02-18  0:17 UTC (permalink / raw)


Adrien Plisson <aplisson-news@stochastique.net> writes:

> anyway, it is a very bad idea since aborting the task may have a side
> effect: you don't know what was going on at the time you abort the task,
> so the state of the data accessed by the task is undefined.

Yes!  If you use abort (including select-then-abort) then any data
modified by the abortable code can be destroyed in an unpredictable
fashion.  So you have to avoid looking at that data after the abort,
or make sure the data is updated in abort-deferred regions, or....
It is *very* difficult to get this right in a large system.

- Bob



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

* Re: Finalization of a record containing a task
  2005-02-18  0:13       ` Robert A Duff
@ 2005-02-18  2:34         ` Randy Brukardt
  2005-02-18 11:27         ` Dmitry A. Kazakov
  1 sibling, 0 replies; 15+ messages in thread
From: Randy Brukardt @ 2005-02-18  2:34 UTC (permalink / raw)


"Robert A Duff" <bobduff@shell01.TheWorld.com> wrote in message
news:wccll9mu3m7.fsf@shell01.TheWorld.com...
...
> >... So in most cases one
> > is forced to use access-to-task components rather than plain task ones.
>
> Yes, but if you declare the object in a library package, you'll
> still have the same problem with finalization happening too late.

Claw uses Ada.Task_Identification.Is_Callable to handle this problem.
(Unfortunately, some compilers get this wrong.) That only works if you
trying to handle this at the library level (having some other solution for
nested objects). In our case, the task is a library-level one that all of
the objects share; the problem is that the task can only close when all of
the objects are gone (as they potentially could use it for executing
commands), but of course the task has to go away before any library-level
objects are finalized.

The solution was to use Ada.Task_Identification.Is_Callable to see if the
main subprogram has completed. If it has, the task exits, allowing
finalization to continue. (Otherwise, a Claw program would hang if objects
are not destroyed by the program, which was common when exceptions were
raised.) This isn't quite a bullet-proof solution; if the main program does
nothing and exits quickly, with all of the real work done by library tasks,
this will cause premature shutdown of the application. (Claw has a way to
turn off this termination for this reason, but the default behavior is less
surprising in the *vast* majority of programs.) This eliminated the
confusing hangs, and made Claw a lot more usable. (We've also got a bunch of
code to detect whether the implementation actually provides a meaningful
result from this function, but hopefully this now works on implementations.)

The trick is to get a task id for the environment task. That can be done by
asking for current task during library-level elaboration by declaring a
constant directly in a library package spec or body:
       Env_Task_Id : constant Ada.Task_Identification.Task_ID :=
             Ada.Task_Identification.Current_Task;
and then using it as needed.

                     Randy.







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

* Re: Finalization of a record containing a task
  2005-02-18  0:13       ` Robert A Duff
  2005-02-18  2:34         ` Randy Brukardt
@ 2005-02-18 11:27         ` Dmitry A. Kazakov
  1 sibling, 0 replies; 15+ messages in thread
From: Dmitry A. Kazakov @ 2005-02-18 11:27 UTC (permalink / raw)


On 17 Feb 2005 19:13:36 -0500, Robert A Duff wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
>>... (Hypothetically, if there were
>> class-wide destructors, one would be able to do that.)
> 
> Sounds interesting.  Can you give details of what you mean?

The idea is to have user-defined initialization / finalization of
class-wide objects. After all, T'Class is as type as T, why shouldn't it
have ctors?

Because presently tagged objects are view-convertible to their classes,
that automatically implies, that even specific tagged objects have to be
initialized / finalized as classes.

So let us have:

type A is new Ada.Finalization....;
type B is new A with ...;

Then, to initialize B, we have to:

1. initialize B (and A from there)
2. initialize B'Class -- presently empty
3. initialize A'Class -- presently empty

To finalize B:

1. finalize A'Class -- presently empty
2. finalize B'Class -- presently empty
3. finalize B (and A from there)

Because now we have something class-wide to dispatch from that could solve
the problem of dispatching from constructors. One will just dispatch from:

Class_Initialize (Object : in out A'Class);

At this stage B (or whatsoever derived object) is fully constructed, so we
can safely dispatch on Object. Same with task components (and
hypothetically with task ancestors): Class_Finalize could call Die_At_Once
entry of its task component.

>>... So in most cases one
>> is forced to use access-to-task components rather than plain task ones.
> 
> Yes, but if you declare the object in a library package, you'll
> still have the same problem with finalization happening too late.

Yes.

----------------------------
Should Ada some day have classes for non-tagged objects, then class-wide
constructors / destructors would be called each time T is converted to
T'Class and back. That would be a real conversion then, which at least adds
/ removes the type tag.

It could become very interesting for by-reference parameter passing,
though.

For example:

   type A is ...;
   procedure Foo (Object : A); -- A is by-reference here

   type B is new A ...;

   X : constant A'Class := Some_B;
begin
   Foo (X);

upon dispatch to A.Foo, X has to be converted to plain A. For this it is
first demoted from class by finalizing A'Class and then B'Class. Now Foo is
ready to call. After Foo's completion, and even if an exception is
propagated, X need to be heighten back to B'Class and A'Class. No matter,
if it is a constant! (:-))

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



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

* Re: Finalization of a record containing a task
  2005-02-16 22:08   ` Robert A Duff
@ 2005-02-21 13:20     ` Craig Carey
  2005-02-21 21:41       ` Robert A Duff
  0 siblings, 1 reply; 15+ messages in thread
From: Craig Carey @ 2005-02-21 13:20 UTC (permalink / raw)


On 16 Feb 2005 17:08:30 -0500, Robert A Duff wrote:
> [...] but you can't mix delay alternatives with
>terminate alternatives.
...
>You also can't mix entry calls with terminate alternatives, so if the
>task wants to talk to a protected object, you're stuck.
...

Are all those restrictions on select statements actually important to the
 language implementors ?.

The current design of select statement seems to be identical to the
 c. 1982 design. Perhaps a redesign could occur.


--  C Carey




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

* Re: Finalization of a record containing a task
  2005-02-21 13:20     ` Craig Carey
@ 2005-02-21 21:41       ` Robert A Duff
  2005-02-22  0:15         ` Randy Brukardt
  0 siblings, 1 reply; 15+ messages in thread
From: Robert A Duff @ 2005-02-21 21:41 UTC (permalink / raw)


Craig Carey <research@ijs.co.nz> writes:

> On 16 Feb 2005 17:08:30 -0500, Robert A Duff wrote:
> > [...] but you can't mix delay alternatives with
> >terminate alternatives.
> ...
> >You also can't mix entry calls with terminate alternatives, so if the
> >task wants to talk to a protected object, you're stuck.
> ...
> 
> Are all those restrictions on select statements actually important to the
>  language implementors ?.
> 
> The current design of select statement seems to be identical to the
>  c. 1982 design. Perhaps a redesign could occur.

From a practical point of view: it's unlikely that any such redesign
will happen any time soon.  The feature set for Ada 2005 is pretty much
closed.  Who knows what the situation will be in 2015?

But it's an interesting question, which I've thought a lot about in the
past.  It's not such much an ease of implementation issue.  The question
is whether the semantics can make sense.

At a high level, "terminate" means, roughly, "if there's no possibility
that I can wake up again, then terminate me."

The Ada 83 rules are constructed so that if all tasks dependent upon a
given master are either terminated or waiting on an open terminate
alternative, then none of them can possibly wake up, so it's safe
to terminate them all.  (One exception: an interrupt could wake them
up, which Ada 83 handles by saying "implementation defined" -- something
of a copout.)

This is based on the fact that tasks can only accept their own entries,
and the fact that you can't call a task's entries without being inside
its master.  (Interrupts can come in from out of the blue.)

But if we allowed:

    select
        X.Some_Entry(...);
    or
        terminate;
    end select;

then X could outlive this task.  So how do we know when it's safe to
terminate this task?  I'd be interested in hearing any ideas -- it seems
like it could be a useful capability, if the rules could make sense!

X could be a task or a protected object.  Requeue makes it even more
"interesting".  ;-)  Imagine the call being requeued here, there,
and elsewhere, unbeknownst to the caller.

I think a delay statement is similar to an entry call -- you can think
of it as a call on a protected object's entry, such that the barrier
will become true at some time.  So if the problem can be solved for
entry calls, it can be solved for delay alternatives.

----

Note that it was proposed during Ada 9X to allow multiple entry calls,
as in:

    select
        X.Some_Entry(...);
    or
        Y.Some_Other_Entry(...); -- illegal!
    end select;

which would mean pick whichever entry call becomes ready first
(similar to the multiple accept case).
Sadly, that didn't make it into Ada 95 (nor Ada 2005).
I don't know of any semantic anomalies; I think it was just
considered too hard to implement.

- Bob



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

* Re: Finalization of a record containing a task
  2005-02-21 21:41       ` Robert A Duff
@ 2005-02-22  0:15         ` Randy Brukardt
  0 siblings, 0 replies; 15+ messages in thread
From: Randy Brukardt @ 2005-02-22  0:15 UTC (permalink / raw)


"Robert A Duff" <bobduff@shell01.TheWorld.com> wrote in message
news:wccll9h4mm6.fsf@shell01.TheWorld.com...
...
> Note that it was proposed during Ada 9X to allow multiple entry calls,
> as in:
>
>     select
>         X.Some_Entry(...);
>     or
>         Y.Some_Other_Entry(...); -- illegal!
>     end select;
>
> which would mean pick whichever entry call becomes ready first
> (similar to the multiple accept case).
> Sadly, that didn't make it into Ada 95 (nor Ada 2005).
> I don't know of any semantic anomalies; I think it was just
> considered too hard to implement.

Nothing sad about it. The Ada 9X office required all of the U-I teams to
make a detailed report. I remember spending a lot of time on that. The
conclusion of all of the reports was that it would have ended being
implemented as a busy-wait loop on virtually all systems, and thus it would
look cheap while the implementation would have been very expensive. (And the
users that were clamoring for it, the hard real-time people, would have
never actually used it in practice.) Those reports are still available
somewhere in the archives (archive.adaic.com).

Specifically, the calls above would end up implemented similarly to:

   loop
     select
         X.Some_Entry(...);
     else null;
     end select;
     select
         Y.Some_Other_Entry(...); -- illegal!
     else null;
     end select;
     delay <some appropriate amount>; -- Let other tasks run.
  end loop;

so you might as well write the above if it really is needed.

                         Randy.






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

end of thread, other threads:[~2005-02-22  0:15 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-02-16 20:35 Finalization of a record containing a task Bj?rn
2005-02-16 20:49 ` Stephen Leake
2005-02-16 21:20   ` Adrien Plisson
2005-02-16 22:09     ` Robert A Duff
2005-02-17  8:24       ` Adrien Plisson
2005-02-18  0:17         ` Robert A Duff
2005-02-17  9:13     ` Dmitry A. Kazakov
2005-02-18  0:13       ` Robert A Duff
2005-02-18  2:34         ` Randy Brukardt
2005-02-18 11:27         ` Dmitry A. Kazakov
2005-02-16 22:08   ` Robert A Duff
2005-02-21 13:20     ` Craig Carey
2005-02-21 21:41       ` Robert A Duff
2005-02-22  0:15         ` Randy Brukardt
2005-02-17 17:50   ` Bj?rn

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