comp.lang.ada
 help / color / mirror / Atom feed
* Dynamic dispatch again
@ 2001-10-06  1:08 Darren New
  2001-10-06 17:52 ` tmoran
  0 siblings, 1 reply; 18+ messages in thread
From: Darren New @ 2001-10-06  1:08 UTC (permalink / raw)


OK, I'm still trying to figure out how to make one entry call wind up
calling code in different units.

It seems to me that if I have a line that says

  requeue thing.do_it

then there's only one unit in which this can be requeued. That is,
there's no way to declare "thing" such that it might point to different
implementations of something, as it could if it was a tagged type. 

Basically, what I'm looking for is a method whereby a task can wait for
one of N entries to be called, take action as appropriate, and have a
driver program have M of these tasks but all running different code. 

Essentially, I have a basic manager task doing I/O, distributing
messages to "profiles". Each "profile" can get the same set of messages
(start, stop, are you ready, here's a message), but each responds to the
messages in different ways. Some of the calls need to be blocking as
well (like "are you ready") or the programming's gonna be a bear.
There's also a *different* task managing requests from the local side of
the connection.

What I have so far is here, but I can't figure out how to properly
define "profile" such that "dispatch task" or "driver" can call either
"profile_double" or "profile_negative" and get the right answer. Is
there any possibility I'm missing something? I'd hate to have to package
up the entire interface as message-passing when there's routines like
"ask everyone if they're ready", and have to pick up the answers coming
back asynchronously.

Thanks in advance!



+=+=+=+ profile.ads

package profile is
    type profile is tagged limited null record;
    type profile_pointer is access all profile'class;
end profile;

+=+=+=+ profile_double.ads

with profile;

package profile_double is

  task type profile_double_task is 
    entry xlate(in_in : in integer; out_out : out integer);
  end profile_double_task;

  type profile_double is new profile.profile with record 
    tsk : profile_double_task; 
  end record;

end profile_double;

+=+=+=+ profile_double.adb

with profile;

package body profile_double is

    task body profile_double_task is 
    begin
        loop
            select
                accept xlate(in_in : in integer; out_out : out integer)
do
                    out_out := in_in + in_in;
                end xlate;
            or
                terminate;
            end select;
        end loop;
    end profile_double_task;

end profile_double;

+=+=+=+ profile_negate.ads

with profile;

package profile_negate is

  task type profile_negate_task is 
    entry xlate(in_in : in integer; out_out : out integer);
  end profile_negate_task;

  type profile_negate is new profile.profile with record 
    tsk : profile_negate_task; 
  end record;

end profile_negate;

+=+=+=+ profile_negate.adb

with profile;

package body profile_negate is

    task body profile_negate_task is 
    begin
        loop
            select
                accept xlate(in_in : in integer; out_out : out integer)
do
                    out_out := - in_in;
                end xlate;
            or
                terminate;
            end select;
        end loop;
    end profile_negate_task;

end profile_negate;

+=+=+=+ dispatch_task.ads

with Profile;
package Dispatch_Task is

  task type Dispatch_Task is
    entry set_task(p : in profile.profile_pointer);
    entry xlate(in_in : in Integer; out_out : out integer);
  end Dispatch_Task;

  type Dispatch_task_pointer is access all dispatch_task;

end Dispatch_Task;

+=+=+=+ dispatch_task.adb

with profile;

package body dispatch_task is

  task body dispatch_task is
    held_p : profile.Profile_Pointer;
  begin
    accept set_task(p : in profile.Profile_Pointer) do
        held_p := p;
    end set_task;
    loop
        select
            accept xlate(in_in : in integer; out_out : out integer) do
                requeue held_p.tsk;
            end xlate;
        or 
            terminate;
        end select;
    end loop;
  end dispatch_task;

end dispatch_task;

+=+=+=+ driver.adb

with dispatch_task;
with profile;
with profile_double;
with profile_negate;
with Ada.Text_IO;

procedure driver is
    type pat is array(1..2) of dispatch_task.dispatch_task_pointer;
    pa : pat;
    i : integer;
begin
    pa(1) := new dispatch_task.dispatch_task;
    pa(2) := new dispatch_task.dispatch_task;
    pa(1).set_task(new profile_double.profile_double);
    pa(2).set_task(new profile_negate.profile_negate);
    pa(1).xlate(100, i);
    Ada.Text_IO.Put_Line("First, " & Integer'Image(I));
    pa(2).xlate(100, i);
    Ada.Text_IO.Put_Line("Second, " & Integer'Image(I));
end driver;

+=+=+=+



-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
                   Who is this Dr. Ibid anyway, 
                  and how does he know so much?



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

* Re: Dynamic dispatch again
  2001-10-06  1:08 Dynamic dispatch again Darren New
@ 2001-10-06 17:52 ` tmoran
  2001-10-08  1:13   ` Darren New
  0 siblings, 1 reply; 18+ messages in thread
From: tmoran @ 2001-10-06 17:52 UTC (permalink / raw)


>OK, I'm still trying to figure out how to make one entry call wind up
>calling code in different units.
>...
>Essentially, I have a basic manager task doing I/O, distributing
>messages to "profiles". Each "profile" can get the same set of messages
>(start, stop, are you ready, here's a message), but each responds to the
>messages in different ways.

How about having a single "profile" task, and using tagged type "methods"
to respond in different ways, eg:

package profile is

  type changeable_content is abstract tagged limited null record;
  type changeable_content_pointer is access all changeable_content'class;

  procedure xlate_content(what_to_do : in changeable_content;
                          in_in : in integer; out_out : out integer)
      is abstract;

  task type general_task(content : changeable_content_pointer) is
    entry xlate(in_in : in integer; out_out : out integer);
  end general_task;
  type general_task_pointer is access general_task;

end profile;

package body profile is

    task body general_task is
    begin
        loop
            select
                accept xlate(in_in : in integer; out_out : out integer)
do
                    xlate_content(content.all, in_in, out_out);
                end xlate;
            or
                terminate;
            end select;
        end loop;
    end general_task;

end profile;



with profile;
package double is

  type content is new profile.changeable_content with null record;

  procedure xlate_content(what_to_do : in content;
                          in_in : in integer; out_out : out integer);

end double;

package body double is

  procedure xlate_content(what_to_do : in content;
                          in_in : in integer; out_out : out integer) is
  begin
      out_out := in_in + in_in;
  end xlate_content;

end double;




with profile;
package negate is

  type content is new profile.changeable_content with null record;

  procedure xlate_content(what_to_do : in content;
                          in_in : in integer; out_out : out integer);

end negate;

package body negate is

  procedure xlate_content(what_to_do : in content;
                          in_in : in integer; out_out : out integer) is
  begin
      out_out := - in_in;
  end xlate_content;

end negate;



with profile;
with double;
with negate;
with Ada.Text_IO;

procedure driver is
    type pat is array(1..2) of profile.general_task_pointer;
    pa : pat;
    i : integer;
    doubler : profile.changeable_content_pointer := new double.content;
    negater : profile.changeable_content_pointer := new negate.content;
begin
    pa(1) := new profile.general_task(doubler);
    pa(2) := new profile.general_task(negater);
    pa(1).xlate(100, i);
    Ada.Text_IO.Put_Line("First, " & Integer'Image(I));
    pa(2).xlate(100, i);
    Ada.Text_IO.Put_Line("Second, " & Integer'Image(I));
end driver;



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

* Re: Dynamic dispatch again
  2001-10-06 17:52 ` tmoran
@ 2001-10-08  1:13   ` Darren New
  2001-10-08  4:23     ` tmoran
  0 siblings, 1 reply; 18+ messages in thread
From: Darren New @ 2001-10-08  1:13 UTC (permalink / raw)


tmoran@acm.org wrote:
> 
> >OK, I'm still trying to figure out how to make one entry call wind up
> >calling code in different units.
> >...
> >Essentially, I have a basic manager task doing I/O, distributing
> >messages to "profiles". Each "profile" can get the same set of messages
> >(start, stop, are you ready, here's a message), but each responds to the
> >messages in different ways.
> 
> How about having a single "profile" task, and using tagged type "methods"
> to respond in different ways, eg:

I thought about this. The problem is that profiles are basically
implementing protocols. Here, I only get to have one piece of code per
profile execute per message. I.e., now I only get one accept per unit. I
can't do something like

begin
  accept initialize(params : in ...; ready : out boolean) do
    ...
  end accept;
  accept connection(params : ...) do
    -- set up connection
  end accept;
  send_message(...); -- peer receiving open is first to send message
  accept recv_message(...) do
    -- Process first answer
  end accept;
  stick_in_database(...);
  accept recv_message(...) do
    -- Process second answer
  end accept;
  ...
end;

I instead have to store the state and wait passively for a call to be
made. At this point, I might as well use a protected object, since I
still don't have a thread running in my code.

It would be very difficult, for example, to have the "are you ready to
start?" question answered "yes", followed immediately by starting to do
something. On the other hand, if I could get different entries being
invoked with a dynamic dispatch, something like the above would let me
immediately start sending as soon as the remote side requested a
connection, for example. It would also let me do something like "accept
a message coming in and put it on the screen, or accept a line from the
keyboard and send it over the socket."

But if Ada can't do this, I'm going to have to come up with a different
way of doing it which will be (I fear) far more painful and obtuse. I'm
expecting I'll have to basically package up all the parameters and build
queues of messages, and essentially reimplement all the entry queueing
stuff myself.

-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
                   Who is this Dr. Ibid anyway, 
                  and how does he know so much?



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

* Re: Dynamic dispatch again
  2001-10-08  1:13   ` Darren New
@ 2001-10-08  4:23     ` tmoran
  2001-10-08 16:26       ` Darren New
  0 siblings, 1 reply; 18+ messages in thread
From: tmoran @ 2001-10-08  4:23 UTC (permalink / raw)


>Here, I only get to have one piece of code per
>profile execute per message. I.e., now I only get one accept per unit. I
>can't do something like
>...
>I instead have to store the state and wait passively for a call to be made.
  Huh?  What's wrong with replacing the single changeable xlate with a
bunch of changeable accept's and other calls, eg:

package profile is

  type changeable_content is abstract tagged limited null record;
  type changeable_content_pointer is access all changeable_content'class;

--   procedure xlate_content(what_to_do : in changeable_content;
--                           in_in : in integer; out_out : out integer)
--       is abstract;
    procedure initialize_content(what_to_do : in changeable_content;
                                 params : in ...; ready : out boolean)
      is abstract;
    procedure connection_content(what_to_do : in changeable_content;
                                 params : ...)
      is abstract;
    procedure send_message_content(what_to_do : in changeable_content;
                                   ...)
      is abstract;
    procedure recv_message_content(what_to_do : in changeable_content;
                                   ...)
      is abstract;
    procedure stick_in_database_content(what_to_do : in changeable_content;
                                        ...)
      is abstract;

  task type general_task(content : changeable_content_pointer) is
--     entry xlate(in_in : in integer; out_out : out integer);
    entry initialize(params : in ...; ready : out boolean);
    entry connection(params : ...);
    entry recv_message(...);
  end general_task;
  type general_task_pointer is access general_task;

end profile;

package body profile is

    task body general_task is
    begin
--         loop
--             select
--                 accept xlate(in_in : in integer; out_out : out integer)
-- do
--                     xlate_content(content.all, in_in, out_out);
--                 end xlate;
--             or
--                 terminate;
--             end select;
--         end loop;
      accept initialize(params : in ...; ready : out boolean) do
        initialize_content(content.all, params, ready);
      end accept;
      accept connection(params : ...) do
        -- set up connection
        connection_content(content.all, params);
      end accept;
      send_message(...); -- peer receiving open is first to send message
      send_message_content(content.all, ...);
      accept recv_message(...) do
        -- Process first answer
        recv_message_content(content.all, ...);
      end accept;
      stick_in_database(...);
      stick_in_database_content(content.all, ...);
      accept recv_message(...) do
        -- Process second answer
        recv_message_content(content.all, ...);
      end accept;
      ...
    end general_task;

end profile;



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

* Re: Dynamic dispatch again
  2001-10-08  4:23     ` tmoran
@ 2001-10-08 16:26       ` Darren New
  2001-10-08 18:21         ` tmoran
  0 siblings, 1 reply; 18+ messages in thread
From: Darren New @ 2001-10-08 16:26 UTC (permalink / raw)


tmoran@acm.org wrote:
> 
> >Here, I only get to have one piece of code per
> >profile execute per message. I.e., now I only get one accept per unit. I
> >can't do something like
> >...
> >I instead have to store the state and wait passively for a call to be made.
>   Huh?  What's wrong with replacing the single changeable xlate with a
> bunch of changeable accept's and other calls, eg:

Because not all profiles will (for example) send a message as the first
thing they do. For example, the way you have it coded, it will always
call send_message after accepting a connection, and nothing I can put in
a "profile" unit is going to change that.

a profile might, for example, receive three messages, then send one and
receive on in a loop, until it gets one that says it should receive
three more. Just as an example.

If I have only a single task, it has a fixed order for accepting
messages in.

Basically, I'm trying to build a state machine, but I have only a fixed
number of states (the select or unary-accept statements) and a fixed
number of transitions from each state. What you're offering is a way to
do something different on each transition. I'm trying, instead, to
actually have different transitions.

In other words, I might want "profile_negate" to do something like

accept first(...);
loop
  select
    accept second(...)
  or
    accept third(...)
    if something_about_third_very_negate_specific then
      send_message(...)
    end if;
  end
end

while "profile_double" does something like
accept first(...)
  send a message
  call an entry somewhere else
end accept;
accept second(...) -- not third
accept second(...)
accept second(...)
accept third(...)


Offering a method to replace only the bodies of the entries without
replacing their structure is like saying "you can generate any
executable code you want from the Ada grammar" when I'm asking for
something like YACC. :-)


Another thought I had was to have a "linking" task. It would just be a
select in a loop, doing something like

loop
  select
    accept first_left(i1 : in integer; o1 : out integer) do
      accept first_indication_right(i2 : out integer) do
        i2:=i1;
      end accept;
      accept first_response_right(o2 : in integer) do
        o1:=o2;
      end accept;
    end accept;
  or
    accept second_left(i7 : in integer) do
      accept second_right(i8 : out integer) do
        i8 := i7;
      end accept;
    end accept;
  end select;
end loop;

Then the side that would be originating the messages (the
driver/dispatcher) would be able to call things like first_left(27, x);
and where the profile would normally have "accept first_left(...)" it
would have to have "first_indication_right(i); new_i := 2*i;
first_response_right(new_i);" instead of the obvious accept mechanism.
In other words, this would turn an entry call/accept into a pair of
entry calls with a dispatcher in the middle matching them up. Seems
horribly ugly, somehow. It also makes it impossible (I think) for a
profile to wait on either of two entries becoming available. That is, it
makes it impossible to say "accept messages, or stop if the `socket
closed' or `user-abort' entries get called.

Is there any way of saying "here's three entry calls, run whichever gets
accepted first" from the caller's side, like there is from the server's
side? Is there any way of having a requeue that requeues to a task type
determined at runtime?

Otherwise, I think I'm going to wind up building all kinds of ugly
hand-rolled entry queues just to make it work. As bad as Java. Ick.

-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
                   Who is this Dr. Ibid anyway, 
                  and how does he know so much?



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

* Re: Dynamic dispatch again
  2001-10-08 16:26       ` Darren New
@ 2001-10-08 18:21         ` tmoran
  2001-10-08 19:07           ` Darren New
  0 siblings, 1 reply; 18+ messages in thread
From: tmoran @ 2001-10-08 18:21 UTC (permalink / raw)


>Because not all profiles will (for example) send a message as the first
>thing they do.
  Ah.  I thought the profile fixed the tasking structure.  But it sounds
like the only required similarity in these tasks is that their spec
(list of "entry"s) is the same.  All else is changeable, correct?
  So as I understand it, you want to be able to do something like:
   task type One is
     entry a(i : in out integer);
     entry b;
   end task;
   task type Two is
     entry a(i : in out integer);
     entry b;
   end task;
   ...
   X : array() of tasks_one_or_two;
   ...
   select
     X(i).b;
   or
     delay 1.0;
   end select;
Is that it?



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

* Re: Dynamic dispatch again
  2001-10-08 18:21         ` tmoran
@ 2001-10-08 19:07           ` Darren New
  2001-10-09  0:25             ` tmoran
  0 siblings, 1 reply; 18+ messages in thread
From: Darren New @ 2001-10-08 19:07 UTC (permalink / raw)


tmoran@acm.org wrote:
> 
> >Because not all profiles will (for example) send a message as the first
> >thing they do.
>   Ah.  I thought the profile fixed the tasking structure.  But it sounds
> like the only required similarity in these tasks is that their spec
> (list of "entry"s) is the same.  All else is changeable, correct?

Yes, ideally. Naturally, some limitations will exist, like
initialization will always happen before finalization. But other than
that, the ability to wait on multiple possible events at each point in
the conversation is important to not rebuilding the whole queueing bit.

>   So as I understand it, you want to be able to do something like:
>    task type One is
>      entry a(i : in out integer);
>      entry b;
>    end task;
>    task type Two is
>      entry a(i : in out integer);
>      entry b;
>    end task;
>    ...
>    X : array() of tasks_one_or_two;
>    ...
>    select
>      X(i).b;
>    or
>      delay 1.0;
>    end select;
> Is that it?

Right. With the understanding that One is going to actually need an
actual thread runnign when it's not in an entry, and wierdness in the
middle (extra levels of indirection, extra tasks that just requeue, etc)
are quite acceptable.

Right now, my alternative is to package up every "entry" as an object of
a tagged type and build a bidirectional queue of such things. Ugly.
Especially since there will be bits of code in the dispatcher that do
things like "ask each profile if it's ready to quit, and if any refuse,
return that refusal, otherwise return ok." A real PITA to do if you have
to queue a bunch of questions, then receive a bunch of answers, then
compare the two lists, waiting for everyone to answer, etc. Been there,
done that (in C), real ugly. :-) I thought with such sophisticated
rendezvous built into Ada, it ought to be easier than that.

-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
                   Who is this Dr. Ibid anyway, 
                  and how does he know so much?



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

* Re: Dynamic dispatch again
  2001-10-08 19:07           ` Darren New
@ 2001-10-09  0:25             ` tmoran
  2001-10-09  0:40               ` Darren New
  0 siblings, 1 reply; 18+ messages in thread
From: tmoran @ 2001-10-09  0:25 UTC (permalink / raw)


>Right now, my alternative is ... .  Ugly.
  I've *always* found that if something was ugly/difficult in Ada,
after some thought it would become clear that the approach was in
fact a poor one, and a different approach, which worked nicely in
Ada, was a better one.  (Not to say that this alternative nice
approach in this case is obvious. ;)



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

* Re: Dynamic dispatch again
  2001-10-09  0:25             ` tmoran
@ 2001-10-09  0:40               ` Darren New
  2001-10-09  1:29                 ` Larry Hazel
  2001-10-09  4:10                 ` tmoran
  0 siblings, 2 replies; 18+ messages in thread
From: Darren New @ 2001-10-09  0:40 UTC (permalink / raw)


tmoran@acm.org wrote:
> 
> >Right now, my alternative is ... .  Ugly.
>   I've *always* found that if something was ugly/difficult in Ada,
> after some thought it would become clear that the approach was in
> fact a poor one, and a different approach, which worked nicely in
> Ada, was a better one.  (Not to say that this alternative nice
> approach in this case is obvious. ;)

Well, that's kind of why I'm posting here. I'm not sure what the right
approach to dynamically dispatching an entry to one of N different tasks
might be. Someone earlier mentioned using "requeue", but that seems to
suffer the same problems. I've thought of any number of different
approaches, and they're all ugly, since the point is to have a bunch of
different tasks running, and all the alternate approaches involve not
dealing with tasks.

It seems Ada isn't quite as orthogonal as could be hoped.

-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
                   Who is this Dr. Ibid anyway, 
                  and how does he know so much?



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

* Re: Dynamic dispatch again
  2001-10-09  0:40               ` Darren New
@ 2001-10-09  1:29                 ` Larry Hazel
  2001-10-09  4:10                 ` tmoran
  1 sibling, 0 replies; 18+ messages in thread
From: Larry Hazel @ 2001-10-09  1:29 UTC (permalink / raw)


Darren New wrote:
> 
> tmoran@acm.org wrote:
> >
> > >Right now, my alternative is ... .  Ugly.
> >   I've *always* found that if something was ugly/difficult in Ada,
> > after some thought it would become clear that the approach was in
> > fact a poor one, and a different approach, which worked nicely in
> > Ada, was a better one.  (Not to say that this alternative nice
> > approach in this case is obvious. ;)
> 
> Well, that's kind of why I'm posting here. I'm not sure what the right
> approach to dynamically dispatching an entry to one of N different tasks
> might be. Someone earlier mentioned using "requeue", but that seems to
> suffer the same problems. I've thought of any number of different
> approaches, and they're all ugly, since the point is to have a bunch of
> different tasks running, and all the alternate approaches involve not
> dealing with tasks.
> 
> It seems Ada isn't quite as orthogonal as could be hoped.

I'm not sure I understand what you are trying to do.  What I am going to suggest
is somewhat ugly to an Ada person and may be compiler dependent, but I don't
think so.  If the task specs are all the same:

with Unchecked_Conversion;
...
...
   task type A is
      -- common spec
   end A;
   type A_Access is access A;

   type Task_Array is array(1..How_Many) of A_Access;

   task type B is
      -- common spec
   end B;
   type B_Access is access B;

   function Coerce is new Unchecked_Conversion (B_Access, A_Access);

etc.

   Tasks : Task_Array := (new A, Coerce(new B), ....);

I've seen this work in Ada 83 with the VADS compiler.

Larry



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

* Re: Dynamic dispatch again
  2001-10-09  0:40               ` Darren New
  2001-10-09  1:29                 ` Larry Hazel
@ 2001-10-09  4:10                 ` tmoran
  2001-10-09 15:40                   ` Darren New
  1 sibling, 1 reply; 18+ messages in thread
From: tmoran @ 2001-10-09  4:10 UTC (permalink / raw)


>approach to dynamically dispatching an entry to one of N different tasks
  If the bodies of the tasks are too heterogeneous, how about the calls
on the tasks.  Are there a small number of subroutines A, B, and C that
do all the calls?  Could you make an A that calls task One, and another
A that is identical except that it replaces all references to One with
references to Two, and so forth.  Then use overloading to tell which A
is being used.  Or have a tagged type determine dynamically which A is
to be used, and thus implicitly which task is to be called.



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

* Re: Dynamic dispatch again
  2001-10-09  4:10                 ` tmoran
@ 2001-10-09 15:40                   ` Darren New
  2001-10-09 17:58                     ` tmoran
  0 siblings, 1 reply; 18+ messages in thread
From: Darren New @ 2001-10-09 15:40 UTC (permalink / raw)


tmoran@acm.org wrote:
> 
> >approach to dynamically dispatching an entry to one of N different tasks
>   If the bodies of the tasks are too heterogeneous, how about the calls
> on the tasks.  Are there a small number of subroutines A, B, and C that
> do all the calls?  Could you make an A that calls task One, and another
> A that is identical except that it replaces all references to One with
> references to Two, and so forth.  Then use overloading to tell which A
> is being used.  Or have a tagged type determine dynamically which A is
> to be used, and thus implicitly which task is to be called.

Basically, what it sounds like you're suggesting is that I wrap the
entry statements inside the polymorphic calls. That is, rather than

accept xxx(...) do
   t.xxx(...) -- polymorphic dispatch
end accept;

I think you're suggesting I do

  procedure t.xxx(...) is -- polymorphic dispatch with
    xxx(...) -- call the entry for this particular task
  end xxx;

I think that's what I'm going to wind up having to do. Basically, I was
trying to avoid having to say things like "this routine should not
block", but rather deal with it with event queuing, selective accept,
and so on. However, it looks like I'm going to have to build a tagged
type and rely on the programmer building in calls to entries or
instantiating queues and such as appropriate. I was hoping to put all
this infrastructure in the library so it would be easier to construct a
configuration. Le sigh. :-)

There are about 30 routines that need to be dynamically dispatched. In
addition to those 30 primitives, there are a number of primitives that I
would want to layer on top that I'd been hoping to build as tasks that
map the original primitives into the same set of primitives. For
example, a task that would receive each fragment of the message until
all fragments had arrived, concatenate them, and present them as if they
arrived in one chunk, or a task that would accept a message, blocking
the caller until it had actually been put on the wire, etc. I can
probably do these with protected objects or so, but again I expect I'll
wind up running into problems with specs and implementations tied too
tightly.

-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
                   Who is this Dr. Ibid anyway, 
                  and how does he know so much?



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

* Re: Dynamic dispatch again
  2001-10-09 15:40                   ` Darren New
@ 2001-10-09 17:58                     ` tmoran
  2001-10-09 19:26                       ` Darren New
  0 siblings, 1 reply; 18+ messages in thread
From: tmoran @ 2001-10-09 17:58 UTC (permalink / raw)


>example, a task that would receive each fragment of the message until
>all fragments had arrived, concatenate them, and present them as if they
>arrived in one chunk, or a task that would accept a message, blocking
>the caller until it had actually been put on the wire, etc. I can
  I'm not sure I understand what you are trying to do.  It sounds like
you want a bunch of low level tasks running various message channels.
Sort of like Ada.Text_IO, Socket_IO, Serial_IO, that kind of thing.
They handle buffering, timing, etc on their respective devices.  Then
the programmer is presented with a single interface where overloading
determines what's called, eg Put_Line(F,...) calls Text_IO if F is an
Ada.Text_IO.File_Type, Socket_IO if it's a Socket_IO.File_Type, etc.
In that scenario the programmer never codes a "select" or a queue,
but calls a routines from your library which handle that stuff.  Is
this closer to what you are driving at?  Ignoring implementation,
can you write down the specifications for the abstraction you want
your users to see.



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

* Re: Dynamic dispatch again
  2001-10-09 17:58                     ` tmoran
@ 2001-10-09 19:26                       ` Darren New
  2001-10-09 19:42                         ` tmoran
  2001-10-09 22:37                         ` tmoran
  0 siblings, 2 replies; 18+ messages in thread
From: Darren New @ 2001-10-09 19:26 UTC (permalink / raw)


tmoran@acm.org wrote:
> 
> >example, a task that would receive each fragment of the message until
> >all fragments had arrived, concatenate them, and present them as if they
> >arrived in one chunk, or a task that would accept a message, blocking
> >the caller until it had actually been put on the wire, etc. I can


>   I'm not sure I understand what you are trying to do.  It sounds like
> you want a bunch of low level tasks running various message channels.

No. It's the high-level tasks running the message channels. 

Think of it this way - pretend I'm implementing a TCP/IP stack in Ada.
As each IP packet comes in, I want to invoke something to do an accept,
deliver data, or indicate an end-of-file. However, each port is going to
indicate a different process, and each of those processes is going to
block (or maybe not!) when it calls read, when it calls open, when it
calls close, etc. Not all invocations of "read" will be from the same
point in a process. Some entries getting invoked might abort other
processing in progress, etc. The interactions between the library I'm
writing (which is low in the protocol stack) and the applications on top
of it (the "profiles" at the top of the protocol stack) are rather more
complex than TCP, but you get the idea. (For full details, RFC3080, or
www.beepcore.org.)

BEEP is similar in what it does to TCP
multiplexing/windowing/buffering/handshaking/etc, except at the
application layer. It's kind of a (ISO-speak) session layer for TCP/IP.

I don't want the TCP-like buffers being managed by the user processes
(i.e., the tasks) because it just isn't elegant. Sure, I can package up
everything and stuff it in queues, and then have the readers dequeue the
message, perhaps blocking until one arrives or not, then dispatching
internally. But it isn't elegant. It's how you have to do it in a lesser
language that doesn't even have proper rendezvous built in.

> Ignoring implementation,
> can you write down the specifications for the abstraction you want
> your users to see.

Sure. It's about 40 pages. :-)  That's a lot of work for something that
can't be implemented, tho. What I'm trying to work out is whether it's
worth writing it all out to start with. Kind of silly to spend a week
writing up a spec I *know* can't be implemented.

-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
                   Who is this Dr. Ibid anyway, 
                  and how does he know so much?



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

* Re: Dynamic dispatch again
  2001-10-09 19:26                       ` Darren New
@ 2001-10-09 19:42                         ` tmoran
  2001-10-09 20:23                           ` Darren New
  2001-10-09 22:37                         ` tmoran
  1 sibling, 1 reply; 18+ messages in thread
From: tmoran @ 2001-10-09 19:42 UTC (permalink / raw)


>writing up a spec I *know* can't be implemented.
  "It's a digital computer.  It can compute anything, eventually." ;)



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

* Re: Dynamic dispatch again
  2001-10-09 19:42                         ` tmoran
@ 2001-10-09 20:23                           ` Darren New
  0 siblings, 0 replies; 18+ messages in thread
From: Darren New @ 2001-10-09 20:23 UTC (permalink / raw)


tmoran@acm.org wrote:
> >writing up a spec I *know* can't be implemented.
>   "It's a digital computer.  It can compute anything, eventually." ;)

Not helpful. Nor true. :-)  Not even true in this case. 

I.e., it's pointless to write a big spec that's missing a piece that
can't be filled in because the language doesn't support it.  What I'm
trying to figure out is whether the language supports it in an unobvious
way, since it seems very strange to me that it wouldn't.

-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
                   Who is this Dr. Ibid anyway, 
                  and how does he know so much?



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

* Re: Dynamic dispatch again
  2001-10-09 19:26                       ` Darren New
  2001-10-09 19:42                         ` tmoran
@ 2001-10-09 22:37                         ` tmoran
  2001-10-10 18:17                           ` Darren New
  1 sibling, 1 reply; 18+ messages in thread
From: tmoran @ 2001-10-09 22:37 UTC (permalink / raw)


> > can you write down the specifications for the abstraction you want
> Sure. It's about 40 pages. :-)  That's a lot of work for something that
  A prospectus would suffice.



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

* Re: Dynamic dispatch again
  2001-10-09 22:37                         ` tmoran
@ 2001-10-10 18:17                           ` Darren New
  0 siblings, 0 replies; 18+ messages in thread
From: Darren New @ 2001-10-10 18:17 UTC (permalink / raw)


tmoran@acm.org wrote:
> 
> > > can you write down the specifications for the abstraction you want
> > Sure. It's about 40 pages. :-)  That's a lot of work for something that
>   A prospectus would suffice.

Well, here's a *very* simplified version of the tasking interface. It's
hard to work these things out completely when you know there are
fundamental stumbling blocks. :-)

My current approach right now involves either defining the "profile
instance" as a tagged type with a pair of queues (for commands coming up
and commands going down) and let you put your own task in that, with the
controller also having a queue and distributing things to the
appropriate instances,

- or -

having profile instances being tagged types with procedures instead of
entries and letting each profile instance implement the procedures as
calls to entries on a task it starts up when instantiated.

Both techniques have both a lot of advantages and a lot of
disadvantages. I'm not yet sure which is better, but I'm starting with
the former and seeing how it starts to look.


+=+=+=+

task type profile_instance_task is

    -- The task is started, then this is called
    -- to find out if it's willing to run.
    entry start(
        Server       : in Server_Name;
        Which_URI    : in URI;
        Result       : out Diagnostic);
        -- Return code 200 for success.

    -- This is called to deliver a frame to the instance.
    entry frame_up(f : in Frame);

    -- This is called to request or force the instance
    -- to close down.
    entry close(
        Who : in (Local, Remote, Socket);
        Why : in (Close, Reset);
        Result : out Diagnostic);

end profile_instance_task;

task type controller_task is

    -- This is called to configure the controller.
    entry setup(
        Reg  : in Registry;
        Sock : in Socket;
        Config : in Configuration);

    -- THE ABOVE IS PART OF THE PROBLEM. The registry has to (basically)
    -- map strings to task bodies, where each task body has the same
    -- entries as profile_instance_task, but where the actual code
    -- executed is different. The problem is that the "accept"
statements
    -- must actually appear inside the task body, and there's only
    -- one task body per task type, and a requeue references an 
    -- object of a specific task type. If I want to accept a call
    -- to frame_up, but call frame_down if I don't get a frame_up 
    -- within 5 seconds, I'm pretty screwed with a task-oriented
    -- interface design. I also cannot do something like 
    -- select close(...) ... then abort loop frame_down(...); end loop;

    -- This is called by the instance to queue a frame.
    entry frame_down(f : in Frame);

    -- This is called by the frame_out_pump to get a frame
    -- to be sent over the socket.
    entry frame_out(f : out Frame);

    -- This is called by the frame_in_pump to deliver a frame
    -- from the socket to the controller.
    entry frame_in(f : in Frame);

    -- This requests the controller start a new profile instance.
    entry start(
        PL : in Profile_List;
        Diag : out Diagnostic;
        Chan : out Integer);

    -- This requests the controller to close a channel or an entire
session.
    entry close(
        Chan : in Integer;
        Diag : out Diagnostic);

end controller_task;



-- 
Darren New 
San Diego, CA, USA (PST). Cryptokeys on demand.
                   Who is this Dr. Ibid anyway, 
                  and how does he know so much?



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

end of thread, other threads:[~2001-10-10 18:17 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-10-06  1:08 Dynamic dispatch again Darren New
2001-10-06 17:52 ` tmoran
2001-10-08  1:13   ` Darren New
2001-10-08  4:23     ` tmoran
2001-10-08 16:26       ` Darren New
2001-10-08 18:21         ` tmoran
2001-10-08 19:07           ` Darren New
2001-10-09  0:25             ` tmoran
2001-10-09  0:40               ` Darren New
2001-10-09  1:29                 ` Larry Hazel
2001-10-09  4:10                 ` tmoran
2001-10-09 15:40                   ` Darren New
2001-10-09 17:58                     ` tmoran
2001-10-09 19:26                       ` Darren New
2001-10-09 19:42                         ` tmoran
2001-10-09 20:23                           ` Darren New
2001-10-09 22:37                         ` tmoran
2001-10-10 18:17                           ` Darren New

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