Hi,

My latest wheel re-invention project has produced a wheel of inferior quality. There's nothing unusual about that, but I'd still like to improve this wheel.

Briefly, I am using sv_setref_pv() to create a perl reference to a structure in a C library (the Gnu MP math library). I don't know that "reference" is precisely the correct term to use - but I'll continue to call it by that name. This reference has been blessed by the sv_setref_pv() call into a package (called 'Math::GnuMP').

I also have perl wrapper functions for just about all of the Gnu MP library functions, and can successfully pass the references to those wrapper functions and have everything work as it should.

There's also a DESTROY() function which seems to work ok.

Occasionally, however, with some scripts that 'use Math::GnuMP;' I get a segfault. The segfault, when it arises, usually occurs just prior to the script exiting. At one stage I put a couple of debug print statements in the DESTROY() function, and they indicate that the segfault occurs *after* all of the references have been destroyed, which is pretty much the last thing that a script does, isn't it ?

I've also had the rarer occasion where a script will segfault when I call one of the wrapper functions. In such instances, there is no DESTROY()ing being conducted. It makes me wonder whether these references that I'm using can somehow start pointing to the wrong place.

I've not managed to pinpoint any of these segfaults down to anything specific. Sometimes the segfault can be avoided by avoiding a certain combination of wrapper functions - though I've not managed to isolate any one particular wrapper function as being responsible for the problem. It always seems to come down to a *combination* of functions.
Other times I've eliminated the segfault by setting $|, or by not using strictures, or by simply importing the specific functions that the script needs rather than importing ':all'. (I've also used even more bizarre and disgraceful methods for eliminating some segfaults.)


I should point out that I'm not using a typemap - I don't know if that's relevant. Another piece of possibly irrelevant information is that, during its lifetime, the structure that the reference refers to may have additional memory allocated to it. When this is necessary it's handled automatically by the GMP library and requires no action on the part of the programmer. I see no reason that this reallocation should pose a problem for the reference, but mention it just in case it might.

Does the above information spark any ideas as to what the problem is (or how it may be resolved).

Is it likely that this is a Gnu MP issue rather than a perl XS one ?
If so, I'll post this to the GMP mailing list (where it will be just as OT as it is here :-)


By way of elaboration, I provide the following (mostly way OT) info. It's a bit longwinded but I've tried to keep it very simple.

Here's what a simple C script that uses the Gnu MP library might look like:

#include <stdlib.h>
#include <stdio.h>
#include <gmp.h>

int main() {
    mpz_t x, y; /* declare */

/* dynamically allocate memory and initialise.
x is set to the value "aaaaaaaaaaaaaaaaaaaa", base 16.
y is set to the value "bbbbbbbbbbbbbbbbbbbb", base 16. */
    mpz_init_set_str(x, "aaaaaaaaaaaaaaaaaaaa", 16);
    mpz_init_set_str(y, "bbbbbbbbbbbbbbbbbbbb", 16);

/* x += y (1st arg = 2nd arg + 3rd arg) */
    mpz_add(x, x, y);

/* x **= 2 (1st arg = 2nd arg * 3rd arg) */
    mpz_mul(x, x, x);

/* print to stdout the value that x contains as
   a base 10 (decimal) number. */
    mpz_out_str(NULL, 10, x);

/* free the memory */
    mpz_clear(x);
    mpz_clear(y);

    return 0;
}


Here's what the corresponding wrapper functions look like. (I write them as Inline::C functions and have Inline::C generate the module's XS file for me.)


---------------------------------------------------------
1) The mpz_init_set_str() wrapper (returns the reference):

SV * Rmpz_create_init(SV * num, SV * base) {
     mpz_t * obj;
     unsigned long b = SvUV(base);

     New(1, obj, sizeof(mpz_t), mpz_t);
     if(obj == NULL) croak("Failed to allocate memory");
     mpz_init_set_str (*obj, SvPV_nolen(num), b);
     return sv_setref_pv(newSViv(0), "Math::GnuMP", obj);
}

---------------------------------------------------------

2) The mpz_add() wrapper

void Rmpz_add(SV * dest, SV * src1, SV * src2) {
     mpz_add(*((mpz_t *) SvIV(SvRV(dest))),
             *((mpz_t *) SvIV(SvRV(src1))),
             *((mpz_t *) SvIV(SvRV(src2))) );
}

--------------------------------------------------------

3) The mpz_mul() wrapper

void Rmpz_mul(SV * dest, SV * src1, SV * src2) {
     mpz_mul(*((mpz_t *) SvIV(SvRV(dest))),
             *((mpz_t *) SvIV(SvRV(src1))),
             *((mpz_t *) SvIV(SvRV(src2))) );
}

---------------------------------------------------------

4) The mpz_out_str() wrapper

void Rmpz_out_str(SV * ref, SV * base) {
     mpz_out_str(NULL, SvUV(base),
                 *((mpz_t *) SvIV(SvRV(ref)));
}

---------------------------------------------------------

5) DESTROY(), which Safefree()s and also wraps mpz_clear()

void DESTROY(SV * ref) {
     mpz_clear(*((mpz_t *) SvIV(SvRV(ref))));
     Safefree((mpz_t *) SvIV(SvRV(ref)));
}

----------------------------------------------------------

And the perl script that mirrors the above C script would simply be:

use strict;
use warnings;
use Math::GnuMP qw(Rmpz_create_init Rmpz_add
                   Rmpz_mul Rmpz_out_str);

my $p1 = Rmpz_create_init("aaaaaaaaaaaaaaaaaaaa", 16);
my $p2 = Rmpz_create_init("bbbbbbbbbbbbbbbbbbbb", 16);
Rmpz_add($p1, $p1, $p2);
Rmpz_mul($p1, $p1, $p1);
Rmpz_out_str($p1, 10);

And that script will run fine, btw, giving the answer of
2864543209168569719679217533134701829179262936025.

I'm sure it was unnecessary, but I also rewrote the above C script in terms of pointers to mpz_t structures, to check that the GMP library handled such things as I expected - which, of course, it did. That rewrite is included below my signature.

If you've got this far, you deserve a medal.
Any corrective advice will be appreciated.

Cheers,
Rob

#include <stdlib.h>
#include <stdio.h>
#include <gmp.h>

int main() {
    mpz_t *x, *y;

    x = malloc(sizeof(mpz_t));
    y = malloc(sizeof(mpz_t));

if(x == NULL || y == NULL) printf("Memory not allocated\n");

    mpz_init_set_str(*x, "aaaaaaaaaaaaaaaaaaaa", 16);
    mpz_init_set_str(*y, "bbbbbbbbbbbbbbbbbbbb", 16);

    mpz_add(*x, *x, *y);
    mpz_mul(*x, *x, *x);
    mpz_out_str(NULL, 10, *x);

    mpz_clear(*x);
    mpz_clear(*y);

    free(x);
    free(y);

    return 0;
}




Reply via email to