comp.lang.ada
 help / color / mirror / Atom feed
* Tasks, Entries, and Variables of a Class-wide type
@ 2010-11-01  5:40 Shark8
  2010-11-01  9:21 ` Dmitry A. Kazakov
  0 siblings, 1 reply; 10+ messages in thread
From: Shark8 @ 2010-11-01  5:40 UTC (permalink / raw)


I have a problem with tasks and class-wide types, namely the
dissallowance of entries returning a value (as a function would)
combined with the requirement that a variable of a class-wide type be
initialized before use.

The problem came up when I was writing a PostScript interpreter and,
implementing the PostScript type-system as a hierarchy of tagged
types, I wanted to be able so separate the parser from the actual
implementation so that it would be possible to have a single parser
service several PostScript interpreters (the state-machine/execution-
context/whatever-you-want-to-call-it).

I’d originally wanted to have something like this:

Task Type Parser_Task is
 Entry Parse( Input : in out String ) Return PostScript_Object’Class;
End Parser;

but that isn’t allowed, but hey, that’s ok because we have out
parameters. We just rearrange it so that we get:

Task Type Parser_Task is
 Entry Parse( Input : in out String; Object : Out
PostScript_Object’Class );
End Parser;

But this doesn’t work because in the calling-code a class-wide
variable needs to be initialized. {and a tag error is raised if you
try to assign an object with a differing tag to the variable}; so:

Declare
    Parser : Parser_Task;
    Some_Object : PostScript_Object’Class:= PostScript_Dummy_Object;
Begin
    Parser.Parse( “5”,  Some_Object ); – Makes Some_Object into a
PS_Integer w/ value of 5.
End;

is invalid because the tags won’t match. We now have the problem where
the class-wide type needs to know what particular object the parser
will return, and the parser needs that class-wide variable wherein to
store the type in the first place. {A programming Catch-22 or Chicken/
Egg problem}

I figured out how to work around this problem, however. Inside the
Parser_Task we can do something like the following:

Task body Parse_Task is
[...]
Accept Parse( Input : in out String; Object : Out
PostScript_Object’Class ) do
   Declare
      Temp : PostScript_Object’Class:= Parse_Function( Input );
     For Temp’Address use Output’Address;
   Begin
      Null;
   End;
End Parse;
[...]

So, now we’re left with the possible ramifications of overriding the
placement of our output variable. The only one that is immediately
obvious to me is that of the possibility of the original dummy class-
variable being over-written by an object that is actually larger.
(I.e. a null-record dummy-object being over written by an object
containing some data like a date/time stamp or somesuch.)

We *can* compensate with making the Dummy_Object object as large as
the largest object that Parser.Parse might return. But I am wondering
if I am missing something here.

{I’m assuming there are perfectly good reasons we can’t have free-
range class-wide types; like that of knowing how much memory to set
aside for the arbitrary and unknown object. And I assume that the same
is true of Task entries being forbidden from returning values [how
much space would be needed for an “Array (Positive Range <>) of
Integer” within the rendevous?]}



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

* Re: Tasks, Entries, and Variables of a Class-wide type
  2010-11-01  5:40 Tasks, Entries, and Variables of a Class-wide type Shark8
@ 2010-11-01  9:21 ` Dmitry A. Kazakov
  2010-11-01 15:30   ` Shark8
  0 siblings, 1 reply; 10+ messages in thread
From: Dmitry A. Kazakov @ 2010-11-01  9:21 UTC (permalink / raw)


On Sun, 31 Oct 2010 22:40:37 -0700 (PDT), Shark8 wrote:

> IοΏ½d originally wanted to have something like this:
> 
> Task Type Parser_Task is
>  Entry Parse( Input : in out String ) Return PostScript_ObjectοΏ½Class;
> End Parser;

This does not make sense, because it would parse during a rendezvous. So
the effect on the caller would be same as if the caller would parse on its
own context. Why not to make parsing a plain function then?

In order to take an advantage of tasking it should be like:

task type Parser_Task is
   entry Request_Parsing (Input : String);
   entry Wait_For_Result (Output : out Post_Script_Object);
end Parser;

> is invalid because the tags wonοΏ½t match. We now have the problem where
> the class-wide type needs to know what particular object the parser
> will return, and the parser needs that class-wide variable wherein to
> store the type in the first place. {A programming Catch-22 or Chicken/
> Egg problem}
> 
> I figured out how to work around this problem, however. Inside the
> Parser_Task we can do something like the following:
> 
> Task body Parse_Task is
> [...]
> Accept Parse( Input : in out String; Object : Out
> PostScript_ObjectοΏ½Class ) do
>    Declare
>       Temp : PostScript_ObjectοΏ½Class:= Parse_Function( Input );
>      For TempοΏ½Address use OutputοΏ½Address;
>    Begin
>       Null;
>    End;
> End Parse;
> [...]

A bad idea.

What you could do is to use the generic dispatching constructor (ARM 3.9).
Here is the outline:

   package Parsers is
      type Parser_Task;
      type Post_Script_Object is tagged null record;
      function Constructor (Parser : not null access Parser_Task)
         return Post_Script_Object;
      task type Parser_Task is
         entry Request (Input : String);
         entry Wait_For_Result (Output : out Tag);
         entry Get_Result (Output : out Post_Script_Object'Class);
      end Parser_Task;
      function Get_Object is
         new Ada.Tags.Generic_Dispatching_Constructor
             (Post_Script_Object, Parser_Task, Constructor);
   end Parsers;

The implementation:

   package body Parsers is
      function Constructor (Parser : not null access Parser_Task)
         return Post_Script_Object is
      begin
         return Result : Post_Script_Object do
            Parser.Get_Result (Result);
         end return;
      end Constructor;

      task body Parser_Task is
         Source : Unbounded_String;
      begin
         loop
            select
               accept Request (Input : String) do
                  Source := To_Unbounded_String (Input);
               end Request;
            or terminate;
            end select;
            -- Here we parsing the stuff in Source. It will create all
            -- sorts of objects derived from Post_Script_Object.
            -- For example:
            declare
               Result : Post_Script_Object'Class :=
                  Post_Script_Object'(null record);
            begin
               accept Wait_For_Result (Output : out Tag) do
                  Output := Result'Tag;
               end Wait_For_Result;
               accept Get_Result (Output : out Post_Script_Object'Class) do
                  Output := Result;
               end Get_Result;
            end;
         end loop;
      end Parser_Task;
   end Parsers;

Note, you will need to override Constructor for each type derived from
Post_Script_Object.

Here is how to use it on the client side:

   Worker : aliased Parser_Task;
begin
   Worker.Request ("...");
   declare
      Result_Type : Tag;
   begin
      Worker.Wait_For_Result (Result); -- Now we know the type
      declare
         Object : Post_Script_Object'Class :=
            Get_Object Result_Type, Worker'Access);
      begin
         -- Doing something with object;
      end;
   end;

Note, you can also pack Wait_For_Result into Constructor, but then you will
not be able to wait for the parser in a timed entry call.

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



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

* Re: Tasks, Entries, and Variables of a Class-wide type
  2010-11-01  9:21 ` Dmitry A. Kazakov
@ 2010-11-01 15:30   ` Shark8
  2010-11-01 18:50     ` Jeffrey Carter
  0 siblings, 1 reply; 10+ messages in thread
From: Shark8 @ 2010-11-01 15:30 UTC (permalink / raw)


On Nov 1, 3:21 am, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
wrote:
> On Sun, 31 Oct 2010 22:40:37 -0700 (PDT), Shark8 wrote:
> > I¢d originally wanted to have something like this:
>
> > Task Type Parser_Task is
> >  Entry Parse( Input : in out String ) Return PostScript_Object¢Class;
> > End Parser;
>
> This does not make sense, because it would parse during a rendezvous.

Crap; you're right!
While I certainly like the idea of having a task-construct within the
language itself, I'm still quite new to using them in-practice.
Thanks for pointing that out.

> So
> the effect on the caller would be same as if the caller would parse on its
> own context. Why not to make parsing a plain function then?

See above; programmer error due to not [fully] understanding the tool
he's using.

>
> In order to take an advantage of tasking it should be like:
>
> task type Parser_Task is
>    entry Request_Parsing (Input : String);
>    entry Wait_For_Result (Output : out Post_Script_Object);
> end Parser;
>

[SNIP]

>
> What you could do is to use the generic dispatching constructor (ARM 3.9).
> Here is the outline:
>
>    package Parsers is
>       type Parser_Task;
>       type Post_Script_Object is tagged null record;
>       function Constructor (Parser : not null access Parser_Task)
>          return Post_Script_Object;
>       task type Parser_Task is
>          entry Request (Input : String);
>          entry Wait_For_Result (Output : out Tag);
>          entry Get_Result (Output : out Post_Script_Object'Class);
>       end Parser_Task;
>       function Get_Object is
>          new Ada.Tags.Generic_Dispatching_Constructor
>              (Post_Script_Object, Parser_Task, Constructor);
>    end Parsers;
>
> The implementation:
>
>    package body Parsers is
>       function Constructor (Parser : not null access Parser_Task)
>          return Post_Script_Object is
>       begin
>          return Result : Post_Script_Object do
>             Parser.Get_Result (Result);
>          end return;
>       end Constructor;
>
>       task body Parser_Task is
>          Source : Unbounded_String;
>       begin
>          loop
>             select
>                accept Request (Input : String) do
>                   Source := To_Unbounded_String (Input);
>                end Request;
>             or terminate;
>             end select;
>             -- Here we parsing the stuff in Source. It will create all
>             -- sorts of objects derived from Post_Script_Object.
>             -- For example:
>             declare
>                Result : Post_Script_Object'Class :=
>                   Post_Script_Object'(null record);
>             begin
>                accept Wait_For_Result (Output : out Tag) do
>                   Output := Result'Tag;
>                end Wait_For_Result;
>                accept Get_Result (Output : out Post_Script_Object'Class) do
>                   Output := Result;
>                end Get_Result;
>             end;
>          end loop;
>       end Parser_Task;
>    end Parsers;
>
> Note, you will need to override Constructor for each type derived from
> Post_Script_Object.
>
> Here is how to use it on the client side:
>
>    Worker : aliased Parser_Task;
> begin
>    Worker.Request ("...");
>    declare
>       Result_Type : Tag;
>    begin
>       Worker.Wait_For_Result (Result); -- Now we know the type
>       declare
>          Object : Post_Script_Object'Class :=
>             Get_Object Result_Type, Worker'Access);
>       begin
>          -- Doing something with object;
>       end;
>    end;

Ah... I get it. By putting the actual parsing between ACCEPT
statements you're allowing the structure within the task from
encountering the problem of multiple tasks with one calling
Wait_for_Object after another [different task] had called Request
which could/would have happened if you had something like:

Task body ....
  Working_Text     : Unbounded_String;
  Temporary_Object : Access PostScript_Object'Class;  --- Just a
holder for the operations...
....
begin
 loop
  Select
    Accept Request(Input_String : In String) do
            Working_Test:= To_Unbounded_String( Input_String );
    end Request;
   OR
    Accept Wait_For_Object( Output : out Tag ) do
            Output:= Temporary_Object'Tag;
    end Wait_For_Object;
   OR
    Get_Result( Output : out PostScript_Object'Class ) do
            Output:= Temporary_Object;
    end Wait_For_Object;
  End Select;
 end loop;
end;


Which, sad to admit, was how I was thinking it would have to be done.
WAAAAY over-complicated [because it would need some state-variables
and blocking-logic] compared to the solution you presented.
So, thank you again for the insight/learning.



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

* Re: Tasks, Entries, and Variables of a Class-wide type
  2010-11-01 15:30   ` Shark8
@ 2010-11-01 18:50     ` Jeffrey Carter
  2010-11-01 19:23       ` Dmitry A. Kazakov
  2010-11-01 19:28       ` Shark8
  0 siblings, 2 replies; 10+ messages in thread
From: Jeffrey Carter @ 2010-11-01 18:50 UTC (permalink / raw)


On 11/01/2010 08:30 AM, Shark8 wrote:
>
> Crap; you're right!
> While I certainly like the idea of having a task-construct within the
> language itself, I'm still quite new to using them in-practice.
> Thanks for pointing that out.

Interestingly, your original problem does not exist if you use composition 
rather than type extension.

-- 
Jeff Carter
"Why don't you bore a hole in yourself and let the sap run out?"
Horse Feathers
49



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

* Re: Tasks, Entries, and Variables of a Class-wide type
  2010-11-01 18:50     ` Jeffrey Carter
@ 2010-11-01 19:23       ` Dmitry A. Kazakov
  2010-11-01 20:08         ` Georg Bauhaus
  2010-11-01 19:28       ` Shark8
  1 sibling, 1 reply; 10+ messages in thread
From: Dmitry A. Kazakov @ 2010-11-01 19:23 UTC (permalink / raw)


On Mon, 01 Nov 2010 11:50:44 -0700, Jeffrey Carter wrote:

> On 11/01/2010 08:30 AM, Shark8 wrote:
>>
>> Crap; you're right!
>> While I certainly like the idea of having a task-construct within the
>> language itself, I'm still quite new to using them in-practice.
>> Thanks for pointing that out.
> 
> Interestingly, your original problem does not exist if you use composition 
> rather than type extension.

No, the original problem is that an object of unconstrained type cannot be
returned from an entry point. That is independent on whether the constraint
is the type tag, discriminant without a default, or array bounds.

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



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

* Re: Tasks, Entries, and Variables of a Class-wide type
  2010-11-01 18:50     ` Jeffrey Carter
  2010-11-01 19:23       ` Dmitry A. Kazakov
@ 2010-11-01 19:28       ` Shark8
  2010-11-01 20:49         ` Jeffrey Carter
  2010-11-05 12:43         ` Robert A Duff
  1 sibling, 2 replies; 10+ messages in thread
From: Shark8 @ 2010-11-01 19:28 UTC (permalink / raw)


On Nov 1, 12:50 pm, Jeffrey Carter
<spam.jrcarter....@spam.not.acm.org> wrote:
> On 11/01/2010 08:30 AM, Shark8 wrote:
>
>
>
> > Crap; you're right!
> > While I certainly like the idea of having a task-construct within the
> > language itself, I'm still quite new to using them in-practice.
> > Thanks for pointing that out.
>
> Interestingly, your original problem does not exist if you use composition
> rather than type extension.
>
> --
> Jeff Carter
> "Why don't you bore a hole in yourself and let the sap run out?"
> Horse Feathers
> 49

Ah, you mean something like a Variant-record? Or am I misunderstanding
you?

Type Object_Type is ( ps_Interger, ps_Character, ps_Name [etc] );
Type PostScript_Object( Type_Discriminant : Object_Type ) is Record
  Case Type_Discriminant is
    When ps_Interger  => Integer_Value : Integer;
    When ps_Character => Character_Value : Character;
    When ps_Name      => String_Value: String; -- Not allowed,
unconstrained type
  end case;
end record;

In the above the solution of an Access_String would be unsuitable if
the parser-task was running on a separate machine [or, more
specifically/correctly, a separate address-space... FireWire networks
can have a memory map which allows devices to access one other's
memories].



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

* Re: Tasks, Entries, and Variables of a Class-wide type
  2010-11-01 19:23       ` Dmitry A. Kazakov
@ 2010-11-01 20:08         ` Georg Bauhaus
  2010-11-01 20:56           ` Dmitry A. Kazakov
  0 siblings, 1 reply; 10+ messages in thread
From: Georg Bauhaus @ 2010-11-01 20:08 UTC (permalink / raw)


On 11/1/10 8:23 PM, Dmitry A. Kazakov wrote:
> On Mon, 01 Nov 2010 11:50:44 -0700, Jeffrey Carter wrote:
>
>> On 11/01/2010 08:30 AM, Shark8 wrote:
>>>
>>> Crap; you're right!
>>> While I certainly like the idea of having a task-construct within the
>>> language itself, I'm still quite new to using them in-practice.
>>> Thanks for pointing that out.
>>
>> Interestingly, your original problem does not exist if you use composition
>> rather than type extension.
>
> No, the original problem is that an object of unconstrained type cannot be
> returned from an entry point. That is independent on whether the constraint
> is the type tag, discriminant without a default, or array bounds.
>

One may, however, pass data indirectly, and return a pointer
or other designating value.

(Or have the task trigger the actions to be performed on
the new object without passing it around; instead, pass
the actions.)



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

* Re: Tasks, Entries, and Variables of a Class-wide type
  2010-11-01 19:28       ` Shark8
@ 2010-11-01 20:49         ` Jeffrey Carter
  2010-11-05 12:43         ` Robert A Duff
  1 sibling, 0 replies; 10+ messages in thread
From: Jeffrey Carter @ 2010-11-01 20:49 UTC (permalink / raw)


On 11/01/2010 12:28 PM, Shark8 wrote:
>
> Ah, you mean something like a Variant-record? Or am I misunderstanding
> you?
>
> Type Object_Type is ( ps_Interger, ps_Character, ps_Name [etc] );
> Type PostScript_Object( Type_Discriminant : Object_Type ) is Record
>    Case Type_Discriminant is
>      When ps_Interger  =>  Integer_Value : Integer;
>      When ps_Character =>  Character_Value : Character;
>      When ps_Name      =>  String_Value: String; -- Not allowed,
> unconstrained type
>    end case;
> end record;

Unbounded_String would be suitable, though. And you want a default on the 
discriminant, so that you can declare an unconstrained object, pass it as the 
actual to an out-mode parameter, and allow it to be assigned to with a different 
discriminant.

-- 
Jeff Carter
"Why don't you bore a hole in yourself and let the sap run out?"
Horse Feathers
49



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

* Re: Tasks, Entries, and Variables of a Class-wide type
  2010-11-01 20:08         ` Georg Bauhaus
@ 2010-11-01 20:56           ` Dmitry A. Kazakov
  0 siblings, 0 replies; 10+ messages in thread
From: Dmitry A. Kazakov @ 2010-11-01 20:56 UTC (permalink / raw)


On Mon, 01 Nov 2010 21:08:37 +0100, Georg Bauhaus wrote:

> One may, however, pass data indirectly, and return a pointer
> or other designating value.

Yes, but it does not look natural with non-limited types. It is strange
that you cannot return, say, String from an entry point.

BTW, shared objects might be prohibitive for certain architectures, e.g.
when tasks run on different processors, which do not share memory. You
would really like to marshal all rendezvous parameters.

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



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

* Re: Tasks, Entries, and Variables of a Class-wide type
  2010-11-01 19:28       ` Shark8
  2010-11-01 20:49         ` Jeffrey Carter
@ 2010-11-05 12:43         ` Robert A Duff
  1 sibling, 0 replies; 10+ messages in thread
From: Robert A Duff @ 2010-11-05 12:43 UTC (permalink / raw)


Shark8 <onewingedshark@gmail.com> writes:

> In the above the solution of an Access_String would be unsuitable if
> the parser-task was running on a separate machine [or, more
> specifically/correctly, a separate address-space... FireWire networks
> can have a memory map which allows devices to access one other's
> memories].

Ada tasks require a shared address space.

I once implemented Ada tasking across multiple machines
(Ada 83), but it was a subset of Ada -- some restrictions
are required.  That was before the Distributed Systems
Annex (which does not use tasks) was invented.

- Bob



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

end of thread, other threads:[~2010-11-05 12:43 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-11-01  5:40 Tasks, Entries, and Variables of a Class-wide type Shark8
2010-11-01  9:21 ` Dmitry A. Kazakov
2010-11-01 15:30   ` Shark8
2010-11-01 18:50     ` Jeffrey Carter
2010-11-01 19:23       ` Dmitry A. Kazakov
2010-11-01 20:08         ` Georg Bauhaus
2010-11-01 20:56           ` Dmitry A. Kazakov
2010-11-01 19:28       ` Shark8
2010-11-01 20:49         ` Jeffrey Carter
2010-11-05 12:43         ` Robert A Duff

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