PerlFFI / FFI-Platypus-Lang-CPP

Documentation and tools for using Platypus with the C++ programming language
Other
4 stars 1 forks source link

Calling the same closure repeatedly consumes memory #8

Closed pipcet closed 9 years ago

pipcet commented 9 years ago

Test case:

use FFI::Platypus;
use FFI::TinyCC;

sub c {
}

my $tcc = FFI::TinyCC->new;

$tcc->compile_string(<<'EOF');
void c_with_arg(void (*f)(void))
{
    f();
}
EOF

my $ffi = FFI::Platypus->new;
$ffi->type('()->void', 'FType');

my $c = $ffi->closure(\&c);

my $a = $tcc->get_symbol('c_with_arg');

while(1) {
    $ffi->function($a => ['FType'] => 'int')->call($c);
}

When run, memory usage keeps increasing until the process is killed when the ulimit is hit. The expected result is that memory usage does not increase indefinitely.

It's not quite a memory leak, since we can reclaim memory by destroying the closure.

It's easy enough to see what causes this: the %cbdata hash in ::Closure keeps growing, since we call add_data for each invocation but never remove payload data.

However, there's no obvious (to me) fix. We could presumably get away with reusing the same ::ClosureData between invocations, but that would break the (pathological?) case in which the same ::Closure is used for different types of function pointer. A more complete fix would attach types to ::Closures.

pipcet commented 9 years ago

Sorry, this is an issue in FFI::Platypus, not FFI::Platypus::Lang::CPP.

plicease commented 9 years ago

This is not a use case that I considered.

In the very least it deserves a CAVEAT. One fix might be to cache the closure data based on the signature. Another that works Right Now to to cast the closure to an opaque like this:

use FFI::Platypus;
use FFI::TinyCC;

sub c {
}

my $tcc = FFI::TinyCC->new;

$tcc->compile_string(<<'EOF');
void c_with_arg(void (*f)(void))
{
    f();
}
EOF

my $ffi = FFI::Platypus->new;
$ffi->type('()->void', 'FType');

my $c = $ffi->closure(\&c);
my $c_ptr = $ffi->cast('()->void' => 'opaque', $c);

my $a = $tcc->get_symbol('c_with_arg');

while(1) {
    $ffi->function($a => ['opaque'] => 'int')->call($c_ptr);
}

That unfortunately pushes extra work on the developer to juggle both the clousre object and the closure pointer and closures are already hard enough to get right with FFI.

plicease commented 9 years ago

closing here, feel free to continue here:

https://github.com/plicease/FFI-Platypus/issues/40