Getting random and wrong values with C interface for a Struct with dynamic data and custom postcircumfix

204 views Asked by At

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.
0

There are 0 answers