comp.lang.ada
 help / color / mirror / Atom feed
* Task with access to itself?
@ 2012-12-05 13:53 Jacob Sparre Andersen
  2012-12-05 14:18 ` Dmitry A. Kazakov
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Jacob Sparre Andersen @ 2012-12-05 13:53 UTC (permalink / raw)


I would like to maintain a collection of tasks, where the tasks
themselves can register their availability.

My immediate thought was to let the tasks know their own address
('Access), such that they can simply pass that to the collection of
available tasks, when they are ready.  But I haven't found a nice way to
implement that pattern, so may be I should do it a different way.

   type Handler is
   type Reference is access all Handler;

   task type Handler is
      entry Set (Self : in     Reference);
      entry Do_Stuff (...);
   end Handler;

   task body Handler is
      Pointer : Reference;
   begin
      accept Set (Self : in     Reference) do
         Pointer := Self;
      end Set;

      loop
         accept Do_Stuff (...) do
            ...
         end Do_Stuff;

         ...

         Register_As_Available (Pointer);
      end loop;
   end Handler;

Any suggestions?

Jacob
-- 
... but, following long-established custom, it is Laplace's result
that is always called, in the modern litterature, "Bayes' theorem." (Jaynes)



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

* Re: Task with access to itself?
  2012-12-05 13:53 Task with access to itself? Jacob Sparre Andersen
@ 2012-12-05 14:18 ` Dmitry A. Kazakov
  2012-12-05 16:57 ` Jeffrey Carter
  2012-12-06  9:38 ` Egil Høvik
  2 siblings, 0 replies; 10+ messages in thread
From: Dmitry A. Kazakov @ 2012-12-05 14:18 UTC (permalink / raw)


On Wed, 05 Dec 2012 14:53:53 +0100, Jacob Sparre Andersen wrote:

> I would like to maintain a collection of tasks, where the tasks
> themselves can register their availability.
[...]
> Any suggestions?

Active object?

   type Active_Object is
      abstract new Ada.Finalization.Limited_Controlled with private;
   overriding procedure Initialize (Object : in out Active_Object);
   overriding procedure Finalize (Object : in out Active_Object);
   procedure Do_Stuff (Object : in out Active_Object) is abstract;
private
   task type Worker (Object : not null access Active_Object'Class) is
       entry Stop;
   end Worker;
   type Active_Object is
      abstract new Ada.Finalization.Limited_Controlled with
   record
       Handler : not null access Worker :=
          new Worker (Active_Object'Access);
   end record;
-------------------------------------------------------------------------------------------
   procedure Initialize (Object : in out Active_Object) is
   begin
        -- Register Object or else its task
   end Initialize;

   procedure Finalize (Object : in out Active_Object) is
       type Worker_Ptr is access all Worker;
       Ptr : Worker_Ptr;
       procedure Free is
           new Ada.Unchecked_Deallocation (Worker, Worker_Ptr)
   begin
       -- Unregister

       Object.Handler.Stop;
       while not Object.Handler'Terminated loop
           delay 0.001;
       end loop;
       Ptr := Object.Handler.all'Unchecked_Access;
       Free (Ptr);
   end Finalize;

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



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

* Re: Task with access to itself?
  2012-12-05 13:53 Task with access to itself? Jacob Sparre Andersen
  2012-12-05 14:18 ` Dmitry A. Kazakov
@ 2012-12-05 16:57 ` Jeffrey Carter
  2012-12-11 11:21   ` Jacob Sparre Andersen
  2012-12-06  9:38 ` Egil Høvik
  2 siblings, 1 reply; 10+ messages in thread
From: Jeffrey Carter @ 2012-12-05 16:57 UTC (permalink / raw)


On 12/05/2012 06:53 AM, Jacob Sparre Andersen wrote:
> I would like to maintain a collection of tasks, where the tasks
> themselves can register their availability.
>
> My immediate thought was to let the tasks know their own address
> ('Access), such that they can simply pass that to the collection of
> available tasks, when they are ready.  But I haven't found a nice way to
> implement that pattern, so may be I should do it a different way.

We make extensive use of a similar pattern, but rather than having the tasks 
register their availability and be given work to do, we have the tasks get their 
work from a protected queue. The task body tends to look like

Forever : loop
    exit Forever when Finalization.Control.Finalizing; -- Protected function

    select
       Work_Queue.Get (Item => Work_Item);
       -- Blocks until something is on the queue
       Process (Job_Info => Work_Item);
    or
       delay ...;
    end select;
end loop Forever;

-- 
Jeff Carter
"If I could find a sheriff who so offends the citizens of Rock
Ridge that his very appearance would drive them out of town ...
but where would I find such a man? Why am I asking you?"
Blazing Saddles
37



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

* Re: Task with access to itself?
  2012-12-05 13:53 Task with access to itself? Jacob Sparre Andersen
  2012-12-05 14:18 ` Dmitry A. Kazakov
  2012-12-05 16:57 ` Jeffrey Carter
@ 2012-12-06  9:38 ` Egil Høvik
  2012-12-06 19:53   ` Adam Beneschan
  2 siblings, 1 reply; 10+ messages in thread
From: Egil Høvik @ 2012-12-06  9:38 UTC (permalink / raw)


On Wednesday, December 5, 2012 2:53:53 PM UTC+1, Jacob Sparre Andersen wrote:
> I would like to maintain a collection of tasks, where the tasks
> 
> themselves can register their availability.
> 
> 
> 
> My immediate thought was to let the tasks know their own address
> 
> ('Access), such that they can simply pass that to the collection of
> 
> available tasks, when they are ready.  But I haven't found a nice way to
> 
> implement that pattern, so may be I should do it a different way.
> 

Having the tasks grabbing jobs from a protected objects is probably a better idea, but this seems to work on GNAT Pro 7.0.2:

package foo is

   task type Handler is
      entry Do_Stuff;
   end Handler;
   
   type Reference is access all Handler;
   
   procedure Register_As_Available(P : access Handler);
   function Get_Next return Reference;

end foo;


with Ada.Task_Identification;
with Ada.Text_IO;

package body foo is

   task body Handler is
   begin
      Register_As_Available(Handler'Access); -- Handler here refers to the current instance of the type

      loop
         select 
            accept Do_Stuff do
               Ada.Text_IO.Put_Line("foo" & Ada.Task_Identification.Image(Handler'Identity));
            end Do_Stuff;
         or 
            terminate;
         end select;
      end loop;
   end Handler;


   List : array(1..2) of Reference;
   Last : Natural := 0;
   
   procedure Register_As_Available(P : access Handler)
   is
   begin
      Last := Last + 1;
      List(Last) := Reference(P);
   end Register_As_Available;


   Next : Natural := 0;
   function Get_Next return Reference is
   begin
      Next := Next + 1;
      if Next > List'Last then
         Next := 1;
      end if;
      return List(Next);
   end Get_Next;
end foo;


with foo;

procedure bar is

   t1 : foo.handler;
   t2 : foo.handler;

   Ref : foo.Reference;
begin

   for i in 1..10 loop
      Ref := foo.Get_Next;
      Ref.Do_Stuff;
   end loop;

end bar;


(If you're not careful, you may end up with a bunch of accessibility issues, though)

-- 
~egilhh



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

* Re: Task with access to itself?
  2012-12-06  9:38 ` Egil Høvik
@ 2012-12-06 19:53   ` Adam Beneschan
  0 siblings, 0 replies; 10+ messages in thread
From: Adam Beneschan @ 2012-12-06 19:53 UTC (permalink / raw)


On Thursday, December 6, 2012 1:38:41 AM UTC-8, Egil Høvik wrote:
> On Wednesday, December 5, 2012 2:53:53 PM UTC+1, Jacob Sparre Andersen wrote:

> Having the tasks grabbing jobs from a protected objects is probably a better idea, but this seems to work on GNAT Pro 7.0.2:
> 
> package foo is
> 
>    task type Handler is
>       entry Do_Stuff;
>    end Handler;
> 
>    type Reference is access all Handler;  
> 
>    procedure Register_As_Available(P : access Handler);
>    function Get_Next return Reference;
> 
> end foo;
> 
> 
> with Ada.Task_Identification;
> with Ada.Text_IO;
> 
> package body foo is
> 
>    task body Handler is
>    begin
>       Register_As_Available(Handler'Access); -- Handler here refers to the current instance of the type
>       loop
>          select 
>             accept Do_Stuff do
>                Ada.Text_IO.Put_Line("foo" & Ada.Task_Identification.Image(Handler'Identity));
>             end Do_Stuff;
>          or 
>             terminate;
>          end select;
>       end loop;
>    end Handler;
> 
>    List : array(1..2) of Reference;
>    Last : Natural := 0;
>    
>    procedure Register_As_Available(P : access Handler)
>    is
>    begin
>       Last := Last + 1;
>       List(Last) := Reference(P);
>    end Register_As_Available;
> 
>    Next : Natural := 0;
>    function Get_Next return Reference is
>    begin
>       Next := Next + 1;
>       if Next > List'Last then
>          Next := 1;
>       end if;
>       return List(Next);
>    end Get_Next;
> end foo;
> 
> 
> with foo;
> 
> procedure bar is
> 
>    t1 : foo.handler;
>    t2 : foo.handler;
> 
>    Ref : foo.Reference;
> begin
> 
>    for i in 1..10 loop
>       Ref := foo.Get_Next;
>       Ref.Do_Stuff;
>    end loop;
> 
> end bar;
> 
> 
> (If you're not careful, you may end up with a bunch of accessibility issues, though)

This program *does* have accessibility issues.  It works only because GNAT is known to have problems catching accessibility-level violations.  The accessibility of Handler is deeper than that of the Reference type; therefore, when this is executed:

  Register_As_Available(Handler'Access)

a Constraint_Error should be raised when Register_As_Available converts its parameter to Reference.  To see this, suppose you have a nested procedure that declares its own Nested_Task : foo.Handler.  Nested_Task would put an access to itself in the List array.  Then the nested procedure exits and Nested_Task is no more, but an access to it still lives in the List.  In Ada, if you don't use 'Unchecked_Access or other constructs that let you get around the rules, you can't have an access object that outlives the thing it's pointing to (except when there's a hole in the language; but there isn't, at least in this case).  So that should be enough to convince anyone that this program shouldn't work.

Changing the above to

  Register_As_Available(Handler'Unchecked_Access)

should make things work, although then the programmer has to be careful to write things in a way so that dangling references aren't used.  Also, the above program has some other problems: (1) both tasks could execute Register_As_Available at the same time, which could really screw things up if the both increment Last before either of them stores into the array; and (2) the main subprogram's code could execute before either task has a chance to call Register_As_Available, meaning that Ref could be null and the program could bomb that way.  If your program "seems" to work, it's because you got lucky (in addition to the compiler problem that is causing an accessibility check to be missed).

                         -- Adam




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

* Re: Task with access to itself?
  2012-12-05 16:57 ` Jeffrey Carter
@ 2012-12-11 11:21   ` Jacob Sparre Andersen
  2012-12-11 20:39     ` Jeffrey Carter
  0 siblings, 1 reply; 10+ messages in thread
From: Jacob Sparre Andersen @ 2012-12-11 11:21 UTC (permalink / raw)


Jeffrey Carter wrote:

> We make extensive use of a similar pattern, but rather than having the
> tasks register their availability and be given work to do, we have the
> tasks get their work from a protected queue. The task body tends to
> look like
>
> Forever : loop
>    exit Forever when Finalization.Control.Finalizing; -- Protected function
>
>    select
>       Work_Queue.Get (Item => Work_Item);
>       -- Blocks until something is on the queue
>       Process (Job_Info => Work_Item);
>    or
>       delay ...;
>    end select;
> end loop Forever;

This is a nice pattern, but it lacks two features I would like to have:

 1) Dynamic addition of tasks.
 2) Control over which order the worker tasks are activated in.

Point 2 can of course be managed with an appropriate
modification/addition to the run-time system.

Greetings,

Jacob
-- 
"The current state of knowledge can be summarised thus:
 In the beginning, there was nothing, which exploded."



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

* Re: Task with access to itself?
  2012-12-11 11:21   ` Jacob Sparre Andersen
@ 2012-12-11 20:39     ` Jeffrey Carter
  2012-12-12 20:25       ` Jacob Sparre Andersen
  0 siblings, 1 reply; 10+ messages in thread
From: Jeffrey Carter @ 2012-12-11 20:39 UTC (permalink / raw)


On 12/11/2012 04:21 AM, Jacob Sparre Andersen wrote:
>
> This is a nice pattern, but it lacks two features I would like to have:
>
>   1) Dynamic addition of tasks.

When we have a new job, if there are no tasks waiting on the queue of jobs, we 
allocate a new task.

>   2) Control over which order the worker tasks are activated in.

Why? They're all identical. In our case, we only allocate a task when there's a 
job to do and no existing task to handle it; such tasks are of course activated 
in the order they're created.

-- 
Jeff Carter
"C++ is like jamming a helicopter inside a Miata
and expecting some sort of improvement."
Drew Olbrich
51



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

* Re: Task with access to itself?
  2012-12-11 20:39     ` Jeffrey Carter
@ 2012-12-12 20:25       ` Jacob Sparre Andersen
  2012-12-12 21:11         ` Jeffrey Carter
  0 siblings, 1 reply; 10+ messages in thread
From: Jacob Sparre Andersen @ 2012-12-12 20:25 UTC (permalink / raw)


Jeffrey Carter wrote:
> Jacob Sparre Andersen wrote:

>>   2) Control over which order the worker tasks are activated in.
>
> Why? They're all identical. In our case, we only allocate a task when
> there's a job to do and no existing task to handle it; such tasks are
> of course activated in the order they're created.

Because I prefer to activate the most recently used task first, as it is
likely to be the one which is "closest" to a CPU (i.e. least likely to
be swapped out to disk).

Greetings,

Jacob
-- 
"Being an absolute ruler today was not as simple as people
 thought.  At least, it was not simple if your ambitions
 included being an absolute ruler tomorrow."



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

* Re: Task with access to itself?
  2012-12-12 20:25       ` Jacob Sparre Andersen
@ 2012-12-12 21:11         ` Jeffrey Carter
  2012-12-13  7:20           ` Jacob Sparre Andersen
  0 siblings, 1 reply; 10+ messages in thread
From: Jeffrey Carter @ 2012-12-12 21:11 UTC (permalink / raw)


On 12/12/2012 01:25 PM, Jacob Sparre Andersen wrote:
>
> Because I prefer to activate the most recently used task first, as it is
> likely to be the one which is "closest" to a CPU (i.e. least likely to
> be swapped out to disk).

You're not talking about activation; you're talking about which task handles the 
next job. Our scheme does the opposite of what you want; the task that handles 
the next job is usually the one that has been idle the longest.

-- 
Jeff Carter
"I unclog my nose towards you."
Monty Python & the Holy Grail
11



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

* Re: Task with access to itself?
  2012-12-12 21:11         ` Jeffrey Carter
@ 2012-12-13  7:20           ` Jacob Sparre Andersen
  0 siblings, 0 replies; 10+ messages in thread
From: Jacob Sparre Andersen @ 2012-12-13  7:20 UTC (permalink / raw)


Jeffrey Carter wrote:
> Jacob Sparre Andersen wrote:

>> Because I prefer to activate the most recently used task first, as it
>> is likely to be the one which is "closest" to a CPU (i.e. least
>> likely to be swapped out to disk).
>
> You're not talking about activation;

No, not in the Ada sense of the term.  Sorry for the confusion.

> you're talking about which task handles the next job. Our scheme does
> the opposite of what you want; the task that handles the next job is
> usually the one that has been idle the longest.

That is also what I would expect from a "typical" Ada run-time.

Greetings,

Jacob
-- 
"The universe isn't for the likes of me to understand. I only work here."



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

end of thread, other threads:[~2012-12-13  8:14 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-12-05 13:53 Task with access to itself? Jacob Sparre Andersen
2012-12-05 14:18 ` Dmitry A. Kazakov
2012-12-05 16:57 ` Jeffrey Carter
2012-12-11 11:21   ` Jacob Sparre Andersen
2012-12-11 20:39     ` Jeffrey Carter
2012-12-12 20:25       ` Jacob Sparre Andersen
2012-12-12 21:11         ` Jeffrey Carter
2012-12-13  7:20           ` Jacob Sparre Andersen
2012-12-06  9:38 ` Egil Høvik
2012-12-06 19:53   ` Adam Beneschan

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