Affix
view release on metacpan or search on metacpan
t/093_malloc_lifeline.t view on Meta::CPAN
use v5.40;
use blib;
use Test2::Tools::Affix qw[:all];
use Test2::V0 -no_srand => 1;
use Affix qw[:all];
#
my $orig_destroy = \&Affix::Memory::DESTROY;
my $destroyed = 0;
no warnings qw[redefine prototype];
*Affix::Memory::DESTROY = sub { $destroyed++; $orig_destroy->(@_); };
#
$|++;
#
my $C_CODE = <<'END_C';
#include "std.h"
//ext: .c
#include <stdlib.h>
DLLEXPORT int read_int_from_void_ptr(void* p) {
if (!p) return -999;
return *(int*)p;
}
DLLEXPORT int sum_int_array(int* arr, int count) {
int total = 0;
for (int i = 0; i < count; i++)
total += arr[i];
return total;
}
END_C
#
my $lib_path = compile_ok($C_CODE);
ok $lib_path && -e $lib_path, 'Compiled a test shared library successfully';
affix $lib_path, 'read_int_from_void_ptr', [ Pointer [Void] ], Int;
affix $lib_path, 'sum_int_array', [ Pointer [Int], Int ], Int;
#
subtest 'malloc: memory survives while the pin is alive' => sub {
my $a = malloc(64);
my $a_addr = address($a);
my $collisions = 0;
for ( 1 .. 10_000 ) {
my $p = malloc(64);
$collisions++ if address($p) == $a_addr;
}
is $collisions, 0, 'live block is never handed out again';
};
#
subtest 'malloc: memory is reclaimed once the pin is released' => sub {
my $before = $destroyed;
my $addr;
{
my $p = malloc(64);
$addr = address($p);
}
is $destroyed, $before + 1, 'releasing the pin destroyed the Affix::Memory object and freed the memory';
# Address reuse is an allocator implementation detail (glibc does not
# guarantee prompt reuse and varies with prior heap state), so it is
# diagnostic only; the refcount check above is the actual reclaim proof.
my $reclaimed = 0;
for ( 1 .. 10000 ) {
my $p = malloc(64);
$reclaimed++ if address($p) == $addr;
}
is $destroyed, $before + 1 + 10000, 'every allocation in the reuse loop was destroyed exactly once';
note "freed block address was reused $reclaimed/10000 times";
};
#
subtest 'malloc: free() works across statements' => sub {
ok no_warnings {
my $ptr = malloc(64);
ok $ptr, 'malloc returned a pin';
ok free($ptr), 'free() on malloc pin returns true';
}, 'no "unmanaged pointer" warning';
};
#
subtest 'calloc: zero-initialized and free()-able' => sub {
ok no_warnings {
my $arr = calloc( 4, sizeof Int );
ok $arr, 'calloc returned a pin';
is sum_int_array( $arr, 4 ), 0, 'calloc memory is zero-initialized';
ok free($arr), 'free() on calloc pin returns true';
}, 'no "unmanaged pointer" warning';
};
#
subtest 'realloc: grows and preserves data' => sub {
my $r = calloc( 2, sizeof Int );
ok $r, 'calloc returned a pin';
my $small = cast( $r, Array [ Int, 2 ] );
$small->[0] = 10;
$small->[1] = 20;
ok realloc( $r, 32 ), 'realloc grew to 32 bytes';
my $big = cast( $r, Array [ Int, 8 ] );
is $big->[0], 10, 'data preserved at index 0 after realloc';
is $big->[1], 20, 'data preserved at index 1 after realloc';
# realloc() preserves but does not zero the extension; write every slot
( run in 2.463 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )