Question

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;
Was it helpful?

Solution

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).

OTHER TIPS

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

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.

Even though this topic is old I ran across it looking for something similar myself (my need was allowing a task to pass a handle to itself to a protected hash map storage area that all tasks have access to, in affect a registration).

You can do this in Ada 2005, thought it's not recommended because it disables access checks but it's the only way I've found to have a task generate (or find) it's own handle to pass into call back functions (please note this does not preclude Task_ID passing to check for Termination or Is_Callable):

task type someTask; 
type someTaskAccessor is access someTask;

task body someTask is
   -- Initialize the access type variable as pointing to the same task it's declared in.
   pointerToTask : someTaskAccessor := someTask'Unchecked_Access; --equiv to "this" ptr
begin
 --  pointerToTask = this; --this is unneeded, pointerToTask is already set!
end someTask;

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;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top