comp.lang.ada
 help / color / mirror / Atom feed
* Basic program with tasks goes out of memory
@ 2004-08-05 14:55 Wojtek Narczynski
  2004-08-05 15:18 ` Dmitry A. Kazakov
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Wojtek Narczynski @ 2004-08-05 14:55 UTC (permalink / raw)


Hello,

Any idea why this program (extracted from AdaSockets example)
eventually eats up all RAM? Runtime leak or my ignorance?

Linux - GNAT 3.4.2 20040729 (prerelease), 
Windows - GNAT 3.15p.

Regards,
Wojtek


with Ada.Exceptions;   use Ada.Exceptions;
with Ada.Text_IO;      use Ada.Text_IO;

procedure Listener is

   task type Echo is
      entry Start;
   end Echo;

   task body Echo is

   begin
      select
         accept Start do
            null;
         end Start;
      or
         terminate;
      end select;

   exception
      when others =>
         Put_Line ("Strange error");
   end Echo;

   type Echo_Access is access Echo;
   Dummy : Echo_Access;

begin

   loop
      Dummy := new Echo;
      Dummy.Start;
      -- The statement below merely "delays" the problem
      delay 0.001;
   end loop;

end Listener;



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

* Re: Basic program with tasks goes out of memory
  2004-08-05 14:55 Basic program with tasks goes out of memory Wojtek Narczynski
@ 2004-08-05 15:18 ` Dmitry A. Kazakov
  2004-08-05 16:13 ` Marc A. Criley
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 10+ messages in thread
From: Dmitry A. Kazakov @ 2004-08-05 15:18 UTC (permalink / raw)


On 5 Aug 2004 07:55:27 -0700, Wojtek Narczynski wrote:

> Hello,
> 
> Any idea why this program (extracted from AdaSockets example)
> eventually eats up all RAM? Runtime leak or my ignorance?
> 
> with Ada.Exceptions;   use Ada.Exceptions;
> with Ada.Text_IO;      use Ada.Text_IO;

with Ada.Unchecked_Deallocation;

> procedure Listener is
> 
>    task type Echo is
>       entry Start;
>    end Echo;
> 
>    task body Echo is
> 
>    begin
>       select
>          accept Start do
>             null;
>          end Start;
>       or
>          terminate;
>       end select;
> 
>    exception
>       when others =>
>          Put_Line ("Strange error");
>    end Echo;
> 
>    type Echo_Access is access Echo;

procedure Free is new Ada.Unchecked_Deallocation (Echo, Echo_Access);

>    Dummy : Echo_Access;
> 
> begin
> 
>    loop
>       Dummy := new Echo;
>       Dummy.Start;
>       -- The statement below merely "delays" the problem
>       delay 0.001;

Free (Dummy);

>    end loop;
> 
> end Listener;

You allocate Echo, but never deallocate it.

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



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

* Re: Basic program with tasks goes out of memory
  2004-08-05 14:55 Basic program with tasks goes out of memory Wojtek Narczynski
  2004-08-05 15:18 ` Dmitry A. Kazakov
@ 2004-08-05 16:13 ` Marc A. Criley
  2004-08-06  8:54   ` Jano
  2004-08-06 10:57   ` Wojtek Narczynski
  2004-08-05 18:32 ` Jim Rogers
  2004-08-07  4:04 ` Dmitriy Anisimkov
  3 siblings, 2 replies; 10+ messages in thread
From: Marc A. Criley @ 2004-08-05 16:13 UTC (permalink / raw)


"Wojtek Narczynski" <wojtek@power.com.pl> wrote:

> Any idea why this program (extracted from AdaSockets example)
> eventually eats up all RAM? Runtime leak or my ignorance?

<snip>

Dmitry's fix should take care of your problem.

It seems kinda obvious, but there was a common misunderstanding about task
deallocation shared by numerous Ada 83 programmers, including myself.

The Ada 83 LRM says in 13.10.1 that "If X designates a task object, the call
FREE(X) has no effect on the task designated by the value of this task
object."

Many people--okay, everyone with whom I ever worked with on Ada 83
projects--misinterpreted that to mean that there was no point in calling an
instantiation of Unchecked_Deallocation on a task (since the LRM said it
"has no effect"), and so we often didn't.

The correct interpetation is that calling FREE has no effect on the
functioning of the _task_itself_, but it would certainly have an effect on
the storage _allocated_ for the task. In other words, you have to free a
dynamically allocated task instance to reclaim its storage, just like any
other dynamically allocated object.

Marc A. Criley
McKae Technologies
www.mckae.com

"Dredging up my own programming errors accumulated across 20 years for
others' edification!"





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

* Re: Basic program with tasks goes out of memory
  2004-08-05 14:55 Basic program with tasks goes out of memory Wojtek Narczynski
  2004-08-05 15:18 ` Dmitry A. Kazakov
  2004-08-05 16:13 ` Marc A. Criley
@ 2004-08-05 18:32 ` Jim Rogers
  2004-08-07  4:04 ` Dmitriy Anisimkov
  3 siblings, 0 replies; 10+ messages in thread
From: Jim Rogers @ 2004-08-05 18:32 UTC (permalink / raw)


wojtek@power.com.pl (Wojtek Narczynski) wrote in message news:<5ad0dd8a.0408050655.355fa926@posting.google.com>...
> Hello,
> 
> Any idea why this program (extracted from AdaSockets example)
> eventually eats up all RAM? Runtime leak or my ignorance?

You are dynamically allocating tasks and never deallocating them.
You will always run out of memory if you use dynamic allocation
without deallocation.

Apparently you are under the impression that the memory for a
terminated task is automatically recovered to the heap. This
impression is incorrect. Your program demonstrates the problem.

You should use Unchecked_Deallocation to recover the dynamically
allocated memory from terminated tasks.

Jim Rogers



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

* Re: Basic program with tasks goes out of memory
  2004-08-05 16:13 ` Marc A. Criley
@ 2004-08-06  8:54   ` Jano
  2004-08-06  9:53     ` Jean-Pierre Rosen
  2004-08-06 10:57   ` Wojtek Narczynski
  1 sibling, 1 reply; 10+ messages in thread
From: Jano @ 2004-08-06  8:54 UTC (permalink / raw)


Marc A. Criley wrote:
> "Wojtek Narczynski" <wojtek@power.com.pl> wrote:
> 
> 
>>Any idea why this program (extracted from AdaSockets example)
>>eventually eats up all RAM? Runtime leak or my ignorance?
> 
> 
> <snip>
> 
> Dmitry's fix should take care of your problem.

But if you're using Gnat (and maybe other?) be sure to check that

The_Task'Terminated

is true before freeing. Otherwise the call will nullify your access but 
not free the task memory.

So instead of a single delay you may prefer

-- Inner loop:
loop
    if Dummy'Terminated then
       Free (Dummy);
       exit;
    else
       delay 0.01; -- To not hog CPU
    end if;
end loop;

However I don't know if a task with a select-terminate clause can be 
never in 'Terminated state (realistically, before program finalization) 
if the other entries are callable. It works with tasks which reach their 
end-of-code (as your example after Start is called).

The above isn't a problem for tasks allocated eventually to perform some 
job and then be freed. They should run until completion and then your 
polling task would take care of freeing the resources, no 
select-terminate involved.



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

* Re: Basic program with tasks goes out of memory
  2004-08-06  8:54   ` Jano
@ 2004-08-06  9:53     ` Jean-Pierre Rosen
  2004-08-06 11:23       ` Jano
  2004-08-06 15:26       ` Wojtek Narczynski
  0 siblings, 2 replies; 10+ messages in thread
From: Jean-Pierre Rosen @ 2004-08-06  9:53 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 1006 bytes --]


"Jano" <notelacreas@porfavor.no> a �crit dans le message de news:2ngv8lFnp34U1@uni-berlin.de...
> So instead of a single delay you may prefer
>
> -- Inner loop:
> loop
>     if Dummy'Terminated then
>        Free (Dummy);
>        exit;
>     else
>        delay 0.01; -- To not hog CPU
>     end if;
> end loop;
>
If (like me) you don't like busy waiting, you can do the following:
begin
   Dummy.Never_Accepted;
exception
   when Tasking_Error =>
      null;
end;

Of course, the entry Never_Accepted is never accepted, but the run-time will raise Tasking_Error in waiting tasks when Dummy
completes. (Strictly speaking, if you are on a multi-processor or if the waiting task is of higher priority than Dummy, you should
still have a loop to wait for the task to terminate, but in this case the busy loop would be presumably executed only once).

-- 
---------------------------------------------------------
           J-P. Rosen (rosen@adalog.fr)
Visit Adalog's web site at http://www.adalog.fr





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

* Re: Basic program with tasks goes out of memory
  2004-08-05 16:13 ` Marc A. Criley
  2004-08-06  8:54   ` Jano
@ 2004-08-06 10:57   ` Wojtek Narczynski
  1 sibling, 0 replies; 10+ messages in thread
From: Wojtek Narczynski @ 2004-08-06 10:57 UTC (permalink / raw)


Hello,

Thanks everybody, I corrected my program accordingly, and it works
fine now, 1.2MB of memory usage and holding.

This seems to be a very common mistake. "Concurrency in Ada" by AB &
AW leaks storage of all dynamically allocated tasks, as far as I could
"grep" it by hand. AdaSockets example code also leaks like that.

Regards,
Wojtek



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

* Re: Basic program with tasks goes out of memory
  2004-08-06  9:53     ` Jean-Pierre Rosen
@ 2004-08-06 11:23       ` Jano
  2004-08-06 15:26       ` Wojtek Narczynski
  1 sibling, 0 replies; 10+ messages in thread
From: Jano @ 2004-08-06 11:23 UTC (permalink / raw)


Jean-Pierre Rosen wrote:
> If (like me) you don't like busy waiting, you can do the following:
> begin
>    Dummy.Never_Accepted;
> exception
>    when Tasking_Error =>
>       null;
> end;

> Of course, the entry Never_Accepted is never accepted, but the
> run-time will raise Tasking_Error in waiting tasks when Dummy 
> completes. (Strictly speaking, if you are on a multi-processor or if
> the waiting task is of higher priority than Dummy, you should still
> have a loop to wait for the task to terminate, but in this case the
> busy loop would be presumably executed only once).

Nice trick.



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

* Re: Basic program with tasks goes out of memory
  2004-08-06  9:53     ` Jean-Pierre Rosen
  2004-08-06 11:23       ` Jano
@ 2004-08-06 15:26       ` Wojtek Narczynski
  1 sibling, 0 replies; 10+ messages in thread
From: Wojtek Narczynski @ 2004-08-06 15:26 UTC (permalink / raw)


"Jean-Pierre Rosen" <rosen@adalog.fr> wrote in message 

>> So instead of a single delay you may prefer
>>
>> -- Inner loop:
>> loop
>>     if Dummy'Terminated then
>>        Free (Dummy);
>>        exit;
>>     else
>>        delay 0.01; -- To not hog CPU
>>     end if;
>> end loop;

> If (like me) you don't like busy waiting, you can do the following:
> begin
>    Dummy.Never_Accepted;
> exception
>    when Tasking_Error =>
>       null;
> end;

Yes, if Free (Dummy) is called when the task is not yet terminated,
the program still leaks. I can see that behaviour on an isolated
testcase, but not in my actual code, which probably leaks too slowly.

But in the actual code the idea is to have one task per connection,
which can mean hundreds of tasks. Their time to terminate can be very
different. I don't see how this idea could be extended to hundreds of
tasks. Jano's idea could, but only with polling.

Regards,
Wojtek



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

* Re: Basic program with tasks goes out of memory
  2004-08-05 14:55 Basic program with tasks goes out of memory Wojtek Narczynski
                   ` (2 preceding siblings ...)
  2004-08-05 18:32 ` Jim Rogers
@ 2004-08-07  4:04 ` Dmitriy Anisimkov
  3 siblings, 0 replies; 10+ messages in thread
From: Dmitriy Anisimkov @ 2004-08-07  4:04 UTC (permalink / raw)


wojtek@power.com.pl (Wojtek Narczynski) wrote in message 
>
> Any idea why this program (extracted from AdaSockets example)
> eventually eats up all RAM? Runtime leak or my ignorance?

I think the only way to do not leak memory on dynamically allocated
tasks,
is to have a task for deallocate terminated tasks. And each
dynamically allocated task have to pass its pointer to the Destructor
before termination.

The Destructor have to be like that.
-------------------------------
   task body Destructor is
      Rf : Relay_Access;
      procedure Free is new Ada.Unchecked_Deallocation (Relay,
Relay_Access);
   begin
      loop
         select
            accept Free_Task (R : Relay_Access) do
               Rf := R;
            end Free_Task;
         end select;

         loop
            exit when Rf'Terminated;
            delay 0.001;
         end loop;

         Free (Rf);
      end loop;
   exception when E : others =>
      Unexpected (E, "Destructor");
   end Destructor;
-------------------------
The complete application with task Destructor usage is in the

http://www.ada-ru.org/src/relay.adb

It has a same as tcprelay.adb (from AdaSockets) functionality, but
with dynamically created task deallocation.

It would be good to have a method to wait until task termination like
on entry call. Maybe something like that.
----------------------------------
   task body Destructor is
      Rf : Relay_Access;
      procedure Free is new Ada.Unchecked_Deallocation (Relay,
Relay_Access);
   begin
      loop
         select
            accept Free_Task (R : Relay_Access) do
               Rf := R;
            end Free_Task;
         end select;

         select
            Rf'Wait_Termination;
         or delay 10.0;
            --  Could not wait more.
            null;
         end loop;

         Free (Rf);
      end loop;
   exception when E : others =>
      Unexpected (E, "Destructor");
   end Destructor;
-------------------------



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

end of thread, other threads:[~2004-08-07  4:04 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-08-05 14:55 Basic program with tasks goes out of memory Wojtek Narczynski
2004-08-05 15:18 ` Dmitry A. Kazakov
2004-08-05 16:13 ` Marc A. Criley
2004-08-06  8:54   ` Jano
2004-08-06  9:53     ` Jean-Pierre Rosen
2004-08-06 11:23       ` Jano
2004-08-06 15:26       ` Wojtek Narczynski
2004-08-06 10:57   ` Wojtek Narczynski
2004-08-05 18:32 ` Jim Rogers
2004-08-07  4:04 ` Dmitriy Anisimkov

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