* 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