i have a C file and a Raku file for connection. sometimes i get random and wrong values. files are as folows
C file: it has a struct called CField which Raku code will interact. Importantly it has a dynamically allocated attribute in it which is data. So i do 2 mallocs in make_field and 2 frees in free_field.
// field.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
struct CField {
void* data;
size_t size;
};
void* checker_malloc(size_t n) {
// malloc but checks if allocated fine
void* p = malloc(n);
if (!p) { fprintf(stderr, "can't allocate %zu bytes", n); exit(1); }
return p;
}
struct CField* make_field(void* data, size_t data_size) {
// malloc for all struct
struct CField* new_field = checker_malloc(sizeof *new_field);
// malloc for .data
size_t num_bytes = data_size * sizeof(int32_t);
new_field->data = checker_malloc(num_bytes);
memcpy(new_field->data, data, num_bytes);
new_field->size = data_size;
return new_field;
}
void free_field(struct CField* v) {
free(v->data);
free(v);
}
Raku file: it has a Wrapper around this struct. I define [] operator here for Whatever passed i.e. Wrapper.new(...)[*] is to do somthing special. It essentially makes a new Wrapper out of current ones data.
## Field.rakumod
use NativeCall;
# C side
my constant LIB = "./libfield.so";
class CField is repr("CStruct") {...}
sub make_field(Pointer[void], size_t --> CField) is native(LIB) { * };
sub free_field(CField) is native(LIB) { * };
class CField {
has Pointer $.data;
has size_t $.size;
submethod DESTROY {
free_field(self);
}
}
# Raku side
class Wrapper is export {
has CField $.field;
has Int $.size;
# New from Raku data
multi method new(@data) {
self.bless:
field => make_field(nativecast(Pointer, CArray[int32].new(@data)), @data.elems),
size => @data.elems;
}
# New from already Wrapper
multi method new(Wrapper $other) {
self.bless:
field => make_field($other.field.data, $other.size),
size => $other.size;
}
method as-list {
# to see what is inside
my $nat = nativecast(CArray[int32], $!field.data);
$nat[^$!size].List
}
multi postcircumfix:<[ ]>(Wrapper:D $w, Whatever) is export {
# new Wrapper object around $w, essentially copies it
Wrapper.new($w)
}
}
Now the reporduction to fail it:
$ tree
.
├── Field.rakumod
└── field.c
$ gcc -std=c99 -Wall -fPIC -shared -olibfield.so field.c
$ raku -I. -MField -e'for ^1_000 { my $a = Wrapper.new([1, 2, 3]); my $b = $a[*].as-list; die "$_: {$b.raku}" unless $b eqv (1, 2, 3) }; say "no error"'
no error
$ raku -I. -MField -e'for ^1_000 { my $a = Wrapper.new([1, 2, 3]); my $b = $a[*].as-list; die "$_: {$b.raku}" unless $b eqv (1, 2, 3) }; say "no error"'
446: $(38717712, 0, 3)
in block <unit> at -e line 1
Sometimes it ends with "no error" as expected. But sometimes it dies as seen. Interestingly, if I comment DESTROY method in CField, its always "no error". But then memory leaks right? So what i am doing wrong and how to fix it?
$ raku -v # Ubuntu 20.04
Welcome to Rakudo™ v2023.09.
Implementing the Raku® Programming Language v6.d.
Built on MoarVM version 2023.09.