Freeing Pointers to Tasks


> Say you dynamically create a task using the "new" operator, the task runs
> and terminates normally.  Should you then use an instantiation of
> Unchecked_Deallocation() to free the memory allocated to the pointer to the
> task as you would with any other data type?  Or is that not needed with
> tasks?

Do you really have to deallocate the task?

I prefer to use a storage pool, so that when you're done with an
allocated task, you can put it into an idle state just before returning
it to the pool.  Like this:

  type Node_Type;
  type Node_Access is access Node_Type;

  task type T is
    entry Start_Up;
    entry Shut_Down;
    entry <whatever>;
  end T;

  type Node_T is
    limited record
       O    : T;
       Next : Node_Access;
    end record;


  task body T is
  begin

    <<Idle>> null;

    select
      accept Start_Up;
    or
      terminate;
    end select;


    <<Main>> null;

    ...
    accept Shut_Down;
    goto Idle;
    ...

    goto Main;

  end T;
          

  Free_List : Node_Access;

  function New_Task return Node_Access is
    Node : Node_Access;
  begin
    if Free_List = null then
       Node := new Node_Type;
    else
       Node := Free_List;
       Free_List := Free_List.Next;
       Node.Next := null;
    end if;

    Node.O.Start_Up;   <--!!!
    return Node;
  end New_Task;


  procedure Free (Node : in out Node_Access) is
  begin
    if Node /= null then
       Node.O.Shut_Down;  <--!!!
       
       Node.Next := Free_List;
       Free_List := Node;
    end if;
  end Free;


> Can not find any details on this topic.  I only know that it is a no-no to
> free the pointer as a means of terminating the task.

There are really two entities you have to be aware of: the "task" and
the "task object."

When you declare a task, like this:

   My_Task : My_Task_Type;

you are really declaring a "task object" that is a handle of some kind
to the actual "task."  The task object elaborates at its point of
declaration, but the task itself "activates" later, immediately after
the "begin" keyword of the declarative region.

So My_Task is a task object, not a task.  The task object may only be 4
or 8 bytes -- whatever representation is needed by the compiler to
identify the actual task.

The rule is slightly different for tasks on the heap: the task object
elaborates in the normal way, but the task it designates activates right
away.

So if you do this:

  task type My_Task_Type is ...;

  type My_Task_Access is access My_Task_Type;

  procedure Free is
    new Unchecked_Deallocation (My_Task_Type, My_Task_Access);

  My_Task : My_Task_Access := new My_Task_Type;

and then you later call Free:

  Free (My_Task);

then what you are really freeing is the task object, not the task.
Maybe you freed 4 or 8 bytes worth of task object, but you probably did
not free the memory associated with the task itself.

The task is alive and well thank you very much (unless it has been
terminated), but now you've destroyed your only way to identify it, and
now you can't even make entry calls.

If you want the task to get deallocated as a result of deallocating the
memory for the task object, it requires a run-time that is smart enough
to know that what you're deallocating is a task handle, and should do
some extra work for this special case.

But most memory management implementations just work in terms of raw
bytes, and don't know anything about what's in those bytes.

Tucker had wanted to make it a new language rule that if you free a task
object whose associated task wasn't yet terminated that you'd get
Program_Error.  However, this wasn't backwards compatible.

However, in Ada95, tasks can have a discriminant.  So if you try to
deallocate a non-terminated task that has a discriminant, this task must
have been written using an Ada95 compiler, and in this case the
implementation is allowed to raise Tasking_Error, or Program_Error, or
indeed do nothing.  See RM95 13.11.2 (11-14).

So a little trick you can do, if you really want to deallocate tasks, is
to give the task a dummy discriminant, so the Ada95 rules will apply.
But this is all implementation dependent anyway.  And even if you the
task is terminated, there's still no guarantee that the memory for the
task will get deallocated.

My advice to you is to not deallocate tasks.  Put tasks on a linked list
as I showed above, so that you can reuse tasks that have already been
allocated.  In this way, you avoid having to ask questions like, How do
I free the memory for the task?  The answer is: don't bother trying.

Contributed by: Matthew Heaney
Contributed on: May 24, 1999
License: Public Domain

Back