comp.lang.ada
 help / color / mirror / Atom feed
* Problem with task component
@ 2012-09-19 18:22 Simon Wright
  2012-09-19 18:48 ` Dmitry A. Kazakov
  2012-09-19 19:44 ` Adam Beneschan
  0 siblings, 2 replies; 24+ messages in thread
From: Simon Wright @ 2012-09-19 18:22 UTC (permalink / raw)


If a Limited_Controlled type has a component of a task type, is it wrong
to call an entry of the task from Initialize? When I do, the caller
blocks.

   package Chips is

      type Chip is limited private;

   private

      task type Polling_Task (For_Chip : not null access Chip) is
         entry Start;
         entry Stop;
      end Polling_Task;

      type Chip is new Ada.Finalization.Limited_Controlled with record
         Polling_Interval : Duration := 0.1;
         Poller           : Polling_Task (For_Chip => Chip'Access);
      end record;

      overriding
      procedure Initialize (C : in out Chip);

...

   package body Chips is

      task body Polling_Task is
      begin
         accept Start;
         ...
      end Polling_Task;

      overriding
      procedure Initialize (C : in out Chip)
      is
      begin
         C.Poller.Start;      <=========== sticks here
      end Initialize;

...

If the task won't start executing until its instance is completely
elaborated and initialized, I don't need the entry Start in any case.



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

* Re: Problem with task component
  2012-09-19 18:22 Problem with task component Simon Wright
@ 2012-09-19 18:48 ` Dmitry A. Kazakov
  2012-09-19 20:40   ` Simon Wright
  2012-09-19 19:44 ` Adam Beneschan
  1 sibling, 1 reply; 24+ messages in thread
From: Dmitry A. Kazakov @ 2012-09-19 18:48 UTC (permalink / raw)


On Wed, 19 Sep 2012 19:22:07 +0100, Simon Wright wrote:

> If a Limited_Controlled type has a component of a task type, is it wrong
> to call an entry of the task from Initialize?

Right, this is mandated by 7.6(12), because the alternative is much worse,
in presence of class-wide access discriminant to the container, which most
people want.

The simple rule is that you should never ever have task components which
you need to communicate to upon initialization and/or finalization.

Always use pointers to tasks. But doing so, you should be extremely careful
preventing dispatch in the task body during these actions. Which is the
rationale for 7.6(12).

P.S. I am afraid that all my rants about construction/destruction issues in
Ada were in vain.

P.P.S. The issue with task components falls into the category "class-wide
constructor." Class initialization/finalization is the earliest/latest time
when it is type-safe to communicate with a task that dispatches in its
body.

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



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

* Re: Problem with task component
  2012-09-19 18:22 Problem with task component Simon Wright
  2012-09-19 18:48 ` Dmitry A. Kazakov
@ 2012-09-19 19:44 ` Adam Beneschan
  1 sibling, 0 replies; 24+ messages in thread
From: Adam Beneschan @ 2012-09-19 19:44 UTC (permalink / raw)


On Wednesday, September 19, 2012 11:22:08 AM UTC-7, Simon Wright wrote:
> If a Limited_Controlled type has a component of a task type, is it wrong
> to call an entry of the task from Initialize? When I do, the caller
> blocks.
> 
>    package Chips is
> 
>       type Chip is limited private;
> 
>    private 
> 
>       task type Polling_Task (For_Chip : not null access Chip) is
>          entry Start;
>          entry Stop;
>       end Polling_Task;
> 
>       type Chip is new Ada.Finalization.Limited_Controlled with record
>          Polling_Interval : Duration := 0.1; 
>          Poller           : Polling_Task (For_Chip => Chip'Access);
>       end record;
> 
>       overriding
>       procedure Initialize (C : in out Chip);
> 
> ...
> 
>    package body Chips is
> 
>       task body Polling_Task is
>       begin
>          accept Start;
>          ...
>       end Polling_Task;
> 
>       overriding
>       procedure Initialize (C : in out Chip)
>       is
>       begin
>          C.Poller.Start;      <=========== sticks here
>       end Initialize;
> 
> ...
> 
> If the task won't start executing until its instance is completely
> elaborated and initialized, I don't need the entry Start in any case.

I think the relevant rules are spelled out in 9.2.  This specifies when a task is *activated*, and a task can't accept an entry call until it's been activated.  For a task created by an allocator (including a task that's a component of an allocated object), the task activation is the last thing that the allocator does (and 7.6(12) confirms that it will happen after Initialize).  For a task created by a standalone object, e.g.

   procedure P is
      C : Chip;
      X1 : Some_Other_Object;
      X2 : Another_Object;
      ...
   begin
      ...

C.Poller won't get activated until *all* the declared objects have been initialized, including X1 and X2.

So I think it's always the case that a task won't be activated until the outermost object that encloses it have been completely initialized.  So it looks like you don't need Start.

                            -- Adam



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

* Re: Problem with task component
  2012-09-19 18:48 ` Dmitry A. Kazakov
@ 2012-09-19 20:40   ` Simon Wright
  2012-09-19 21:11     ` Dmitry A. Kazakov
  0 siblings, 1 reply; 24+ messages in thread
From: Simon Wright @ 2012-09-19 20:40 UTC (permalink / raw)


(Thanks also to Adam for helpful discussion)

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

> On Wed, 19 Sep 2012 19:22:07 +0100, Simon Wright wrote:
>
>> If a Limited_Controlled type has a component of a task type, is it
>> wrong to call an entry of the task from Initialize?
>
> Right, this is mandated by 7.6(12), because the alternative is much
> worse, in presence of class-wide access discriminant to the container,
> which most people want.

Not sure I need class-wide, will think about it. Thanks for the
suggestion.

> The simple rule is that you should never ever have task components
> which you need to communicate to upon initialization and/or
> finalization.
>
> Always use pointers to tasks. But doing so, you should be extremely
> careful preventing dispatch in the task body during these
> actions. Which is the rationale for 7.6(12).

Also, it seems to me that if I don't use pointers I'm going to have
trouble finalizing the object: the contained task is sitting there and
prevents leaving the scope in which its containing object is declared,
so the containing object doesn't get finalized and there's no
opportunity for Finalize to tell the task to stop. (And wait until it's
actually terminated before deallocating it. I know that AdaCore have
fixed the feature where deallocating a task that hasn't yet terminated
silently leaves the TCB allocated; does anyone know which FSF GCCs/GNATs
this fix has made it into?)



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

* Re: Problem with task component
  2012-09-19 20:40   ` Simon Wright
@ 2012-09-19 21:11     ` Dmitry A. Kazakov
  2012-09-19 21:48       ` Simon Wright
  0 siblings, 1 reply; 24+ messages in thread
From: Dmitry A. Kazakov @ 2012-09-19 21:11 UTC (permalink / raw)


On Wed, 19 Sep 2012 21:40:40 +0100, Simon Wright wrote:

> Also, it seems to me that if I don't use pointers I'm going to have
> trouble finalizing the object: the contained task is sitting there and
> prevents leaving the scope in which its containing object is declared,
> so the containing object doesn't get finalized and there's no
> opportunity for Finalize to tell the task to stop.

You need an open terminate-alternative ("or terminate;") when the object
containing the task component is about to finalize.

The usual problem is that the terminate-alternative does not live together
with the delay-alternative and/or else-part, which makes it quite a
challenge to implement something like a typical "driver" component task:

task body Driver is
begin
   loop
      do I/O;
      exit/terminate when parent dies;
   end loop;
end Driver;

If the I/O subsystem calls to the task upon I/O completion, you could try
something like this:

task body Driver is
begin
   loop
      Start_IO (Self);
      select
         accept Completed (...);
         ... -- Process the data
      or accept Canceled;
         ... -- I/O was canceled
      or accept Error (...);
         ... -- I/O error
      or terminate; -- Still have troubles killing all outstanding requests
      end select;    -- which may refer to the driver task, that is gone
   end loop;         -- by now
end Driver;

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



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

* Re: Problem with task component
  2012-09-19 21:11     ` Dmitry A. Kazakov
@ 2012-09-19 21:48       ` Simon Wright
  2012-09-24  5:28         ` J-P. Rosen
  0 siblings, 1 reply; 24+ messages in thread
From: Simon Wright @ 2012-09-19 21:48 UTC (permalink / raw)


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

> You need an open terminate-alternative ("or terminate;") when the
> object containing the task component is about to finalize.
>
> The usual problem is that the terminate-alternative does not live
> together with the delay-alternative and/or else-part, which makes it
> quite a challenge to implement something like a typical "driver"
> component task:

Indeed!

This particular application detects discrete I/O changes (switch
open/close) via an MCP23017 i/o multiplexer on a Raspberry Pi.

Using interrupts is in the next phase, for now I'm just polling by
reading the chip's registers so I must delay & can't use terminate. A
pain.



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

* Re: Problem with task component
  2012-09-19 21:48       ` Simon Wright
@ 2012-09-24  5:28         ` J-P. Rosen
  2012-09-24  8:23           ` Simon Wright
  0 siblings, 1 reply; 24+ messages in thread
From: J-P. Rosen @ 2012-09-24  5:28 UTC (permalink / raw)


Le 19/09/2012 23:48, Simon Wright a �crit :
> Using interrupts is in the next phase, for now I'm just polling by
> reading the chip's registers so I must delay & can't use terminate. A
> pain.
I don't understant what you'd want. Terminate is selected when a whole
subsystem is quiet. I you have a delay alternative, a task can be
revived, and in turn revive the whole subsystem. There is no way
terminate could be selected.

-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr



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

* Re: Problem with task component
  2012-09-24  5:28         ` J-P. Rosen
@ 2012-09-24  8:23           ` Simon Wright
  2012-09-24 13:37             ` J-P. Rosen
  2012-09-24 15:44             ` Adam Beneschan
  0 siblings, 2 replies; 24+ messages in thread
From: Simon Wright @ 2012-09-24  8:23 UTC (permalink / raw)


"J-P. Rosen" <rosen@adalog.fr> writes:

> Le 19/09/2012 23:48, Simon Wright a écrit :
>> Using interrupts is in the next phase, for now I'm just polling by
>> reading the chip's registers so I must delay & can't use terminate. A
>> pain.
> I don't understant what you'd want. Terminate is selected when a whole
> subsystem is quiet. I you have a delay alternative, a task can be
> revived, and in turn revive the whole subsystem. There is no way
> terminate could be selected.

The language in ARM9.3 is, now you point it out, quite opaque,
especially if you approach it with wrong assumptions.

The general case (common in embedded systems) is to have a task
executing a loop waiting for something to happen and to need to stop the
task.

As Dmitry and others have pointed out, if the "something" is a network
I/O, you have to arrange to cancel the I/O (eg by closing the socket)
and you can then exit the task.

But, if what you want is to delay, you want to write

   loop
      select
         delay Interval;
         --  do work
      or
         --  exit the loop if there's some reason to stop
      end select;
   end loop;

What I wanted to do was to be able to write 'terminate' and to have the
runtime realise that finalization of the object containing the task
would be a good reason to take the terminate alternative[1].

   [I don't know if that would work if I'd used an accept instead of a
   delay, which would have made the code compilable?????]

Instead, the idiom needs to be

   loop
      select
         delay Interval;
         --  do work
      or
        accept Stop;
        exit;
      end select;
   end loop;

[1] See, for example, the last three paras of John English's book ch19
sec3,
http://faculty.cs.wwu.edu/reedyc/AdaResources/bookhtml/ch19.htm#19.3

   This solution requires the master task to call Shutdown explicitly
   when it wants to terminate the task. The disadvantage with this
   approach is that it’s possible to forget to call Shutdown. A better
   solution is to add a terminate alternative to the select statement:

    task body Spreadsheet_Task is
    begin
        loop
            select
                accept Recalculate;
                Do_Recalculation;
            or
                accept Shutdown;
                exit;
            or
                terminate;
            end select;
        end loop;
    end Spreadsheet_Task;

   The terminate alternative must be the last one in a select statement,
   and it can’t contain anything except a terminate statement like the
   one shown above. When the master task gets to the end of the block
   where the spreadsheet was declared, the spreadsheet task will
   terminate the next time that the select statement is executed (or
   immediately, if the task is already waiting in the select
   statement). This means that the master doesn’t have to do anything
   special to terminate the task, but it can still call Shutdown if it
   wants to terminate the task before the end of the block where it was
   declared.



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

* Re: Problem with task component
  2012-09-24  8:23           ` Simon Wright
@ 2012-09-24 13:37             ` J-P. Rosen
  2012-09-24 14:17               ` Simon Wright
  2012-09-24 17:53               ` Robert A Duff
  2012-09-24 15:44             ` Adam Beneschan
  1 sibling, 2 replies; 24+ messages in thread
From: J-P. Rosen @ 2012-09-24 13:37 UTC (permalink / raw)


Le 24/09/2012 10:23, Simon Wright a écrit :
> The language in ARM9.3 is, now you point it out, quite opaque,
> especially if you approach it with wrong assumptions.
[good explanation snipped]

The principle of the "terminate" is that a task terminates automatically
if it can be proven that its entries cannot be called.

Since a task is not visible outside its master*, any task that can call
T must depend on the same master as T. Therefore, T can terminate if its
master is completed, and every task depending on the master is either
completed or waiting on a terminate alternative.

However, imagine that the master (M) itself is waiting on a terminate
alternative. If M is revived, it could in turn call T. Therefore, the
previous reasoning must be extended to M's master, and so on. I hope
this makes the wording more understandable:

"the open terminate_alternative is selected if and only if the following
conditions are satisfied:
- The task depends on some completed master; and
- Each task that depends on the master considered is either already
terminated or similarly blocked at a select_statement with an open
terminate_alternative.

When both conditions are satisfied, the task considered becomes
completed, together with all tasks that depend on the master considered
that are not yet completed."

Now, if one of the tasks in the whole family is allowed to wake-up due
to expiration of a delay statement, it could call T (directly or
indirectly), and wake up the whole gang. Therefore, terminate could
never be selected for the task that has the delay - implying that
terminate could not be selected for any other task in the family.

------
* except for the infamous "rosen pathology" in Ada 83 that was
exterminated in Ada 95

-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr



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

* Re: Problem with task component
  2012-09-24 13:37             ` J-P. Rosen
@ 2012-09-24 14:17               ` Simon Wright
  2012-09-24 17:53               ` Robert A Duff
  1 sibling, 0 replies; 24+ messages in thread
From: Simon Wright @ 2012-09-24 14:17 UTC (permalink / raw)


"J-P. Rosen" <rosen@adalog.fr> writes:

> The principle of the "terminate" is that a task terminates
> automatically if it can be proven that its entries cannot be called.

If that's the case, why do we need to say so?!

I asked earlier whether a select with an accept/terminate body would
behave as I wanted, and wrote this:

   procedure Termination is
      task type T is
         entry Stop;
      end T;
      type R is limited record
         The_T : T;
      end record;
      task body T is
      begin
         loop
            select
               accept Stop;
            or
               terminate;
            end select;
         end loop;
      end T;
      The_R : R;
   begin
      null;
   end Termination;

which does indeed manage to run to completion and return to the command
line.

On thinking about it, I can see that a whole class of toy programs that
demonstrate tasking would have to replace the 'null;' body above with
'delay 1_000_000.0;' if the language worked the way I had hoped.

Until about 10 years ago, my main procedures were exactly like that!



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

* Re: Problem with task component
  2012-09-24  8:23           ` Simon Wright
  2012-09-24 13:37             ` J-P. Rosen
@ 2012-09-24 15:44             ` Adam Beneschan
  2012-09-24 16:13               ` AdaMagica
  2012-09-24 16:29               ` Dmitry A. Kazakov
  1 sibling, 2 replies; 24+ messages in thread
From: Adam Beneschan @ 2012-09-24 15:44 UTC (permalink / raw)


On Monday, September 24, 2012 1:23:50 AM UTC-7, Simon Wright wrote:

> What I wanted to do was to be able to write 'terminate' and to have the
> runtime realise that finalization of the object containing the task
> would be a good reason to take the terminate alternative[1].

> 
> [1] See, for example, the last three paras of John English's book ch19
> sec3,
> http://faculty.cs.wwu.edu/reedyc/AdaResources/bookhtml/ch19.htm#19.3
> 
>    This solution requires the master task to call Shutdown explicitly
>    when it wants to terminate the task. The disadvantage with this
>    approach is that it’s possible to forget to call Shutdown. A better
>    solution is to add a terminate alternative to the select statement:

I've been trying to think of a way to get around the "forget" problem by using controlled types.  My first thought was to define a controlled record (i.e. derived from Ada.Finalization.Limited_Controlled) that has a task as a component, and have the Finalize routine for the controlled type call the Shutdown entry.  That way, since the shutdown protocol would be defined, in essence, right by the definition of the task type, there's no way a procedure that uses the type could "forget" to call Shutdown.  No good, though; 7.6.1(4) says that the Finalize routine on the record object would be called *after* the task terminates.  So it would be too late.

A couple thoughts: (1) This isn't the first time I've seen a case where it would be useful for a task to be notified that someone wants to finalize it.  There have been several posts to c.l.a in the past by posters who tried Unchecked_Deallocation on an object containing a task, thinking it would shut down the task, and were surprised by the program's behavior.  I'm not convinced that it's a good idea to redefine "terminate" so that an attempt to finalize an object can cause an open terminate alternative to be selected; but, perhaps, it might be useful for a task to define an entry that is "bound" to a finalization attempt, so that if an object containing a task becomes eligible for finalization (i.e. the master on which it depends is being finalized, or an object that contains it is being deallocated), then that entry is called.  Or maybe there are other approaches.  But it does seem to me that this sort of functionality is one that could be useful but that the language makes it difficult to achieve.

(2) Off the top of my head, I don't see any *implementation* reason why a selective accept couldn't have both a delay alternative and a terminate alternative.  It seems like it wouldn't require the runtime library to do anything it doesn't already know how to do.

                        -- Adam



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

* Re: Problem with task component
  2012-09-24 15:44             ` Adam Beneschan
@ 2012-09-24 16:13               ` AdaMagica
  2012-09-24 16:43                 ` Adam Beneschan
  2012-09-24 16:29               ` Dmitry A. Kazakov
  1 sibling, 1 reply; 24+ messages in thread
From: AdaMagica @ 2012-09-24 16:13 UTC (permalink / raw)


On Monday, September 24, 2012 5:44:32 PM UTC+2, Adam Beneschan wrote:
> I've been trying to think of a way to get around the "forget" problem by using controlled types.  My first thought was to define a controlled record (i.e. derived from Ada.Finalization.Limited_Controlled) that has a task as a component, and have the Finalize routine for the controlled type call the Shutdown entry.  That way, since the shutdown protocol would be defined, in essence, right by the definition of the task type, there's no way a procedure that uses the type could "forget" to call Shutdown.  No good, though; 7.6.1(4) says that the Finalize routine on the record object would be called *after* the task terminates.  So it would be too late.

Why not use abort in this case? (In the general case, abort might not work immediately or be dangerous, but under these circumstances? I used it e.g. effectively when a commercial flight simulator program - all tasks inside have infinite loops - had to be stopped.)



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

* Re: Problem with task component
  2012-09-24 15:44             ` Adam Beneschan
  2012-09-24 16:13               ` AdaMagica
@ 2012-09-24 16:29               ` Dmitry A. Kazakov
  2012-09-24 20:49                 ` J-P. Rosen
  1 sibling, 1 reply; 24+ messages in thread
From: Dmitry A. Kazakov @ 2012-09-24 16:29 UTC (permalink / raw)


On Mon, 24 Sep 2012 08:44:32 -0700 (PDT), Adam Beneschan wrote:

> (2) Off the top of my head, I don't see any *implementation* reason why a
> selective accept couldn't have both a delay alternative and a terminate
> alternative.

Else-alternative is no less important than delay. We need something like

   select
      terminate; -- Terminate if requested otherwise continue
   else
      null;
   end select;

> It seems like it wouldn't require the runtime library to do anything it
> doesn't already know how to do.

There is also a lesser issue with terminate. It does not allow a sequence
of statements to execute before termination. The workaround requires quite
clumsy constructs involving controlled types.

--------
I thought about a predefined task entry T'Terminate to determine if
termination requested. It would resolve most of the problems the terminate
alternative has.

We would simply write:

task body T is
begin
   loop
      select
         accept T'Terminate;
         exit;
      or ... whatever
      end select;
   end loop;
end T;

T'Terminate would never be dequeued, i.e. could be accepted as many times
as necessary.

If allowed to be called explicitly, it could also be used as an unobtrusive
task abort request, with the advantage that it could be requested using a
timed entry call:

   select
      T'Terminate;
   or delay 1.0;
      Put_Line ("Something is wrong with T");
      abort T; -- Die already, dog!
   end select;

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



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

* Re: Problem with task component
  2012-09-24 16:13               ` AdaMagica
@ 2012-09-24 16:43                 ` Adam Beneschan
  0 siblings, 0 replies; 24+ messages in thread
From: Adam Beneschan @ 2012-09-24 16:43 UTC (permalink / raw)


On Monday, September 24, 2012 9:13:12 AM UTC-7, AdaMagica wrote:
> On Monday, September 24, 2012 5:44:32 PM UTC+2, Adam Beneschan wrote:
> 
> > I've been trying to think of a way to get around the "forget" problem by using controlled types.  My first thought was to define a controlled record (i.e. derived from Ada.Finalization.Limited_Controlled) that has a task as a component, and have the Finalize routine for the controlled type call the Shutdown entry.  That way, since the shutdown protocol would be defined, in essence, right by the definition of the task type, there's no way a procedure that uses the type could "forget" to call Shutdown.  No good, though; 7.6.1(4) says that the Finalize routine on the record object would be called *after* the task terminates.  So it would be too late.
> 
> 
> 
> Why not use abort in this case? (In the general case, abort might not work immediately or be dangerous, but under these circumstances? I used it e.g. effectively when a commercial flight simulator program - all tasks inside have infinite loops - had to be stopped.)

It wouldn't solve the problem.  Using a Shutdown entry, and requiring that the master call it before it leaves, seems to be the best solution, but the issue is that it requires the master to call the entry, and a coder could forget that (or they could put the Shutdown call at the end of the block but then sabotage themselves by putting a RETURN statement in the middle of the code sequence).  Using an abort statement is no better. 

                          -- Adam




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

* Re: Problem with task component
  2012-09-24 13:37             ` J-P. Rosen
  2012-09-24 14:17               ` Simon Wright
@ 2012-09-24 17:53               ` Robert A Duff
  2012-09-24 20:40                 ` J-P. Rosen
  1 sibling, 1 reply; 24+ messages in thread
From: Robert A Duff @ 2012-09-24 17:53 UTC (permalink / raw)


"J-P. Rosen" <rosen@adalog.fr> writes:

> * except for the infamous "rosen pathology" in Ada 83 that was
> exterminated in Ada 95

Which pathology are you referring to?  Returning a task from
a function?

    function F return Task_Type is
        T: Task_Type;
    begin
        return T;
    end F;

I don't see what that has to do with terminate alternatives,
because T is necessarily terminated when F returns to its
caller.  The problem with that pathology is that it's difficult
to implement without leaking memory.

Or did you mean some other pathology?

- Bob



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

* Re: Problem with task component
  2012-09-24 17:53               ` Robert A Duff
@ 2012-09-24 20:40                 ` J-P. Rosen
  0 siblings, 0 replies; 24+ messages in thread
From: J-P. Rosen @ 2012-09-24 20:40 UTC (permalink / raw)


Le 24/09/2012 19:53, Robert A Duff a �crit :
> "J-P. Rosen" <rosen@adalog.fr> writes:
> 
>> * except for the infamous "rosen pathology" in Ada 83 that was
>> exterminated in Ada 95
> 
> Which pathology are you referring to?  Returning a task from
> a function?
> 
Yes. I mentioned this, because it was the only case where a task could
outlive its master


-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr



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

* Re: Problem with task component
  2012-09-24 16:29               ` Dmitry A. Kazakov
@ 2012-09-24 20:49                 ` J-P. Rosen
  2012-09-25  7:35                   ` Dmitry A. Kazakov
  0 siblings, 1 reply; 24+ messages in thread
From: J-P. Rosen @ 2012-09-24 20:49 UTC (permalink / raw)


Le 24/09/2012 18:29, Dmitry A. Kazakov a �crit :
> Else-alternative is no less important than delay. We need something like
> 
>    select
>       terminate; -- Terminate if requested otherwise continue
>    else
>       null;
>    end select;
Which is of course undecidable.

If the master is on a select-or-terminate, what should the task do? If
it selects the terminate, the whole family terminates, fine. If it
selects the else part, it can call one of the master's entries, wake up
the master who can now call the task's entries: the task was right not
to select the terminate!


-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr



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

* Re: Problem with task component
  2012-09-24 20:49                 ` J-P. Rosen
@ 2012-09-25  7:35                   ` Dmitry A. Kazakov
  2012-09-25 17:52                     ` J-P. Rosen
  0 siblings, 1 reply; 24+ messages in thread
From: Dmitry A. Kazakov @ 2012-09-25  7:35 UTC (permalink / raw)


On Mon, 24 Sep 2012 22:49:07 +0200, J-P. Rosen wrote:

> Le 24/09/2012 18:29, Dmitry A. Kazakov a �crit :
>> Else-alternative is no less important than delay. We need something like
>> 
>>    select
>>       terminate; -- Terminate if requested otherwise continue
>>    else
>>       null;
>>    end select;
> Which is of course undecidable.

Tasks behavior is undecidable most of the time. I dare to say that tasks
were useless when behaved in a decidable manner.

It does not make sense to require something from specifically task
termination, when the rest is not constrained. And it does not work,
anyway, because it is in no way to prevent deadlocking upon task
finalization. Happens all the time in Ada.

> If the master is on a select-or-terminate, what should the task do?

I propose a competing rule:

   Selection of the terminate alternative by the master shall not depend on
the states of the client tasks.

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



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

* Re: Problem with task component
  2012-09-25  7:35                   ` Dmitry A. Kazakov
@ 2012-09-25 17:52                     ` J-P. Rosen
  2012-09-25 18:35                       ` Dmitry A. Kazakov
  0 siblings, 1 reply; 24+ messages in thread
From: J-P. Rosen @ 2012-09-25 17:52 UTC (permalink / raw)


Le 25/09/2012 09:35, Dmitry A. Kazakov a �crit :
> I propose a competing rule:
> 
>    Selection of the terminate alternative by the master shall not depend on
> the states of the client tasks.
But that's the whole purpose of the terminate alternative: to terminate
a task if no client can call it. How can you decide that without
considering the state of client tasks?

You might be considering something that could be useful, but is not
related to the terminate alternative as defined by Ada...

-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr



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

* Re: Problem with task component
  2012-09-25 17:52                     ` J-P. Rosen
@ 2012-09-25 18:35                       ` Dmitry A. Kazakov
  2012-09-25 19:22                         ` J-P. Rosen
  0 siblings, 1 reply; 24+ messages in thread
From: Dmitry A. Kazakov @ 2012-09-25 18:35 UTC (permalink / raw)


On Tue, 25 Sep 2012 19:52:01 +0200, J-P. Rosen wrote:

> Le 25/09/2012 09:35, Dmitry A. Kazakov a �crit :
>> I propose a competing rule:
>> 
>>    Selection of the terminate alternative by the master shall not depend on
>> the states of the client tasks.
> But that's the whole purpose of the terminate alternative: to terminate
> a task if no client can call it. How can you decide that without
> considering the state of client tasks?

I need not to consider that. Termination of a task is normally determined
solely by its master (terminating) and its internal state (open terminate
alternative). I.e. strictly top-down. I understand the motivation of the
schema you described. Unfortunately, this approach seems useless for
real-life design of tasking applications, which rather use the top-down
approach to ensure proper termination of tasks. And clients calling to the
master is a bad idea anyway, a tight coupling etc.

> You might be considering something that could be useful, but is not
> related to the terminate alternative as defined by Ada...

Yes. One could introduce T'Terminate and class-wide destructors leaving
terminate alternatives as-is. A class-wide destructor will simply call to
some user-defined entry to terminate a component task, which most people do
anyway.

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



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

* Re: Problem with task component
  2012-09-25 18:35                       ` Dmitry A. Kazakov
@ 2012-09-25 19:22                         ` J-P. Rosen
  2012-09-26  7:27                           ` Dmitry A. Kazakov
  0 siblings, 1 reply; 24+ messages in thread
From: J-P. Rosen @ 2012-09-25 19:22 UTC (permalink / raw)


Le 25/09/2012 20:35, Dmitry A. Kazakov a �crit :
> I need not to consider that. Termination of a task is normally determined
> solely by its master (terminating) and its internal state (open terminate
> alternative). I.e. strictly top-down.

IIUC, the scheme you have in mind is a server task which, at some point,
either accepts calls from its master, or delays and do some processing,
but you want it to halt if the master is completed. You can write it
this way:

task body Server is
begin
   loop
      select
        accept ... do ... end;
      or delay Time_Out;
         exit when not Master_Task'Callable;
      end select;
      -- do something
   end loop;
end Server;

Note that when the master completes (for whatever reason, including
unhandled exception or abort), it is not terminated since it has to wait
for its dependent tasks, but it is no more callable. That's why you have
to check 'Callable and not 'Terminated.
-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr



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

* Re: Problem with task component
  2012-09-25 19:22                         ` J-P. Rosen
@ 2012-09-26  7:27                           ` Dmitry A. Kazakov
  2012-09-26 11:49                             ` Georg Bauhaus
  0 siblings, 1 reply; 24+ messages in thread
From: Dmitry A. Kazakov @ 2012-09-26  7:27 UTC (permalink / raw)


On Tue, 25 Sep 2012 21:22:37 +0200, J-P. Rosen wrote:

> Le 25/09/2012 20:35, Dmitry A. Kazakov a �crit :
>> I need not to consider that. Termination of a task is normally determined
>> solely by its master (terminating) and its internal state (open terminate
>> alternative). I.e. strictly top-down.
> 
> IIUC, the scheme you have in mind is a server task which, at some point,
> either accepts calls from its master, or delays and do some processing,
> but you want it to halt if the master is completed. You can write it
> this way:
> 
> task body Server is
> begin
>    loop
>       select
>         accept ... do ... end;
>       or delay Time_Out;
>          exit when not Master_Task'Callable;
>       end select;
>       -- do something
>    end loop;
> end Server;
> 
> Note that when the master completes (for whatever reason, including
> unhandled exception or abort), it is not terminated since it has to wait
> for its dependent tasks, but it is no more callable. That's why you have
> to check 'Callable and not 'Terminated.

The weakness of this approach is that the client must know its master. It
is almost never the case. And it is tight coupling from the software design
POV.

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



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

* Re: Problem with task component
  2012-09-26  7:27                           ` Dmitry A. Kazakov
@ 2012-09-26 11:49                             ` Georg Bauhaus
  2012-09-26 14:13                               ` Dmitry A. Kazakov
  0 siblings, 1 reply; 24+ messages in thread
From: Georg Bauhaus @ 2012-09-26 11:49 UTC (permalink / raw)


On 26.09.12 09:27, Dmitry A. Kazakov wrote:

> The weakness of this approach is that the client must know its master. It
> is almost never the case. And it is tight coupling from the software design
> POV.

But from the software design POV, the client, or its programmer,
always does know that there is an operational dependence if the client
should react to the master's life cycle. I don't understand why this
should not be made explicit?

Also, arguably, a master knowing the clients will create tight coupling
too, for it could otherwise be replaced with some other master. This
could be considered better, more flexible software design.

A registry to the rescue?  (I notice that Ravenscar tasks require
a different model entirely...)

So why not create some measure of autonomy by

   type M is task Interface;
   type M_CRef is access constant M'Class;


   task body Server is
      Master_Task : M_CRef;
   begin
      accept Master (Master_Task : M_CRef) do
	 Server.Master_Task := Master_Task;
      end Master;
      loop
	 select
	    accept ... do ... end;
	 or delay Time_Out;
            exit when not Master_Task.all'Callable;
	 end select;
	 -- do something
      end loop;
   end Server;


Server might even switch masters if the first acceptor is moved
to the loop.




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

* Re: Problem with task component
  2012-09-26 11:49                             ` Georg Bauhaus
@ 2012-09-26 14:13                               ` Dmitry A. Kazakov
  0 siblings, 0 replies; 24+ messages in thread
From: Dmitry A. Kazakov @ 2012-09-26 14:13 UTC (permalink / raw)


On Wed, 26 Sep 2012 13:49:09 +0200, Georg Bauhaus wrote:

> On 26.09.12 09:27, Dmitry A. Kazakov wrote:
> 
>> The weakness of this approach is that the client must know its master. It
>> is almost never the case. And it is tight coupling from the software design
>> POV.
> 
> But from the software design POV, the client, or its programmer,
> always does know that there is an operational dependence if the client
> should react to the master's life cycle. I don't understand why this
> should not be made explicit?

Because it is not same as to know who is the master. We have an example at
hand: a task component. Who is the master? It could be any because the
object can be instantiated anywhere.

> Also, arguably, a master knowing the clients will create tight coupling
> too, for it could otherwise be replaced with some other master.

Master must know them at least in order to be able to spawn these tasks. 

The point is that bidirectional exchange between two tasks, e.g. when both
calls entries of each other is a bad idea anyway you turn it. Good design
always tries to have only unidirectional relationships.

>    task body Server is
>       Master_Task : M_CRef;
>    begin
>       accept Master (Master_Task : M_CRef) do

Error-prone and does not work for task components anyway.

What you indeed could do is to add a task attribute T'Master or equivalent
to get the ID of the master and then to use it as:

   Is_Callable (T'Master); -- See Ada.Task.Identification

I don't see how this is cleaner than more explicit constructs for querying
if the task is wished to terminate.

Anyway the problem is not much task termination but construction of
class-wide objects. You want to communicate with a task component that
dispatches on the container's tag upon container initialization and
finalization. That is the core problem. (BTW, note bidirectional
relationship: container -> task (component), task -> container (access
discriminant), which expectedly causes troubles).

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



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

end of thread, other threads:[~2012-09-30  1:18 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-09-19 18:22 Problem with task component Simon Wright
2012-09-19 18:48 ` Dmitry A. Kazakov
2012-09-19 20:40   ` Simon Wright
2012-09-19 21:11     ` Dmitry A. Kazakov
2012-09-19 21:48       ` Simon Wright
2012-09-24  5:28         ` J-P. Rosen
2012-09-24  8:23           ` Simon Wright
2012-09-24 13:37             ` J-P. Rosen
2012-09-24 14:17               ` Simon Wright
2012-09-24 17:53               ` Robert A Duff
2012-09-24 20:40                 ` J-P. Rosen
2012-09-24 15:44             ` Adam Beneschan
2012-09-24 16:13               ` AdaMagica
2012-09-24 16:43                 ` Adam Beneschan
2012-09-24 16:29               ` Dmitry A. Kazakov
2012-09-24 20:49                 ` J-P. Rosen
2012-09-25  7:35                   ` Dmitry A. Kazakov
2012-09-25 17:52                     ` J-P. Rosen
2012-09-25 18:35                       ` Dmitry A. Kazakov
2012-09-25 19:22                         ` J-P. Rosen
2012-09-26  7:27                           ` Dmitry A. Kazakov
2012-09-26 11:49                             ` Georg Bauhaus
2012-09-26 14:13                               ` Dmitry A. Kazakov
2012-09-19 19:44 ` Adam Beneschan

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