tags:

views:

222

answers:

4

Specifically, is there a way for a task to get a reference to itself?

For example:

task type someTask; 
type someTaskAccessor is access someTask;

task body someTask is
    pointerToTask : someTaskAccessor;
begin
    pointerToTask = this;
end someTask;
+3  A: 

The package Ada.Task_Identification provides the Current_Task function to retrieve the current task's Task_ID.

Marc C
Yeah, I saw that, but can you retrieve the pointer with the ID?
Landon
why would you want that ?
Adrien Plisson
Take a look at the package Ada.Task_Attributes (C.7.2 in the Ada 2005 LRM). That let's you associate a generically supplied attribute, such as a task pointer or handle to a block of user-defined data, with an instance of a task.Caveat: I've never personally used this package, I'm just going by the LRM.
Marc C
+2  A: 

the most evident solution i could suggest is to declare a rendez-vous (an entry) at the very beginning of your task, to which you pass a reference to the task just created. the other possibility is using a discriminant to your task type, whose role is to tell a new task where it is located (pass the access to the new task into the discriminant). unfortunately, i don't have an Ada compiler at hand so i can't give you any working example.

anyway, based on your comment: the creation of a new task needs to be handled somewhere, at which point you will also need to determine where this new task will go into your doubly-linked list (you need to know at least one existing task when creating a new one in order for them to communicate: they won't discover themselves magically). you can take advantage of this moment, when you have the newly created task and its left and right peers, to tell everybody who are their neighbour (using a rendez-vous once again).

Adrien Plisson
That's actually the solution I came to shortly after asking this question :)
Landon
Its also the right answer. I'd accept it were I you.
T.E.D.
A: 

I would reorganize your code if I were you. So, there are some tasks which interact with other tasks, now with 2 tasks. And there is linked list, which is responsible for storing the tasks and manage the insertion/deletion of tasks. This is a global object which should handle synchronized.

That's why I advice you to create a protected object, and store the list of tasks inside that. The protected is typically used for passive objects, where some resource must be handle synchronized. You can have procedures like insert, remove etc. This will ensure that only one creation and removal will be running at a time, and the linked list will not be inconsistent.

Each task should know it's "partner" tasks which could change when insertion or removal of a task. I advice the create an entry into the task which will update its neighbors. When tasks come or leave, the protected object will update the neighbors.

In this case, no need to access the "this" pointer, because the protected object will organize everything. Only an ID is needed, which can identify the task (for removal).

I try to write the code, but I do not have compiler now:

task type computer;
type computer_ptr is access all computer;    
task type computer is
 entry init(id:integer);
 entry set_neighbor(left,right:computer_ptr);
end computer;

protected comp_list is
 procedure insert; -- called by organizer
 procedure remove(from:integer); -- called by task
private
 type comp_list is array(integer range<>) of computer_ptr;
 comps:comp_list(1..MAX):=(others=>null); -- or use own structure
end comp_list;

task body computer is
 id_:integer;
 left_n,right_n:computer_ptr:=null;
begin
 accept init(id:integer) do
  id_:=id;
 end init;
 while true loop
  select
   accept set_neighbor(left,right:computer_ptr) do
    left_n:=left;right_n:=right;
   end set_neighbor;
   or
    -- do its work
  end select;
  if (some_condition) then
   comp_list.remove(id_);
   break;
  end if;
 end loop;
end task computer;

protected body comp_list is
 procedure insert is
  p:computer_ptr;
 begin
  p:=new computer;
  -- add to list -> nr;
  p.all.init(nr);
  -- call set_neighbor to its left and itself
 end insert;
 procedure remove(from: integer) is
 begin
  -- remove from list and get its neighbors
  -- call set_neighbor regarding new ones
 end remove;
end comp_list;
Krisztian
+1  A: 

A couple of things here.

First off, Ada does OO differently that C++. There are no "this" pointers in the language. Dispatching is done off of parameters. One implication of this is that it is possible to dispatch off of more than one parameter, unlike in C++. That's another discussion for another time though. If you don't like it, you can always name your dispatching parameter "this".

Secondly, OO concepts don't really apply very well to concurrency objects like tasks. This isn't Ada's fault. It is a well-known problem. Sadly, it was rather unimaginatively termed "The Concurrency Problem", so references to it get swamped with programming issues on a google search. The basic gist is that you can make objects support inheritence and dynamic dispatch and all that good stuff, or you can make them support concurrency. Doing both in the same language structure is very difficult.

As a matter of practicality, if you need a pointer to your own task, you can either make it a global, or have the task that allocates it pass the pointer in using some kind of initilization rendezvous. I've seen this done before, to have a task in a stack of worker tasks put itself back on the "idle" stack when it finishes.

T.E.D.