[gambit-list] Tracking Down a Segfault
Marc Feeley
feeley at iro.umontreal.ca
Fri Sep 5 11:21:35 EDT 2008
On 4-Sep-08, at 10:54 PM, Joel J. Adamson wrote:
> Thanks so much for the tips; I have some follow-up questions:
>
>>>>>> "MM" == Mikael More <mikael.more at gmail.com> writes:
>
> MM> - Throwing exceptions with no handler/catcher may sigsegv your
> MM> app.
>
> Good to know, I was unaware of this.
>
> MM> - You should presuppose that ___argXX variables inside
> MM> Scheme-to-C calls are trashed after C-to-Scheme calls. I.e.: If
> MM> you have declared a c-define or c-lambda that takes parameters,
> MM> these will appear as ___arg0, ___arg1 etc. in the C code of the
> MM> c-define / c-lambda. These will have correct contents all until
> MM> you invoke Scheme functions from the C code. not afterwards.
>
> Okay, take the following functions, that may be the source of the
> problem:
Indeed your code violates some invariants of Gambit. The correct code
is attached below.
The source of the problem is that you are storing references to Scheme
objects (i.e. the ___SCMOBJ type) in places that the garbage collector
has no knowledge of (i.e. C variables), so when there is a GC these
references are not updated when the object referenced is moved by the
GC. [Side note: amusingly the code actually works for f64vectors of
length greater or equal to 128 because those vectors are allocated as
"still" objects that aren't moved by the GC!]
Here is the sequence of events that cause the problem to occur:
1) scm_vector_to_C receives the parameter f64vec which is a movable
f64vector (lets call it V)
2) the code calls the C functions f64vector_length and f64vector_ref
which jump back into Scheme
3) the Scheme code executed when f64vector_ref is called allocates a
flonum
4) at some point the heap fills up with these flonums and the GC is
called
5) ***the GC moves V but does not update f64vec in scm_vector_to_C***
6) when the for loop in scm_vector_to_C continues it calls
f64vector_ref with f64vec, which has a stale reference (it points to
where V used to be)
There are two solutions:
1) call the macro ___F64VECTORREF (from gambit.h) which does not
allocate a flonum, and hence does not invoke the GC
2) use an inline code c-lambda where the vector is passed in one of
the ___argxxx variables. These variables ***are know to the GC and
will be updated properly***. But don't store their content in another
C variable!
Marc
;;;;;; SOLUTION 1
(c-declare #<<end-of-c-declare
#include <stdlib.h>
#ifdef ___ORIGINAL_CODE___
/* C function for transferring vectors */
int f64vector_length (___SCMOBJ);
double f64vector_ref (___SCMOBJ, int);
#endif
double * scm_vector_to_C (___SCMOBJ f64vec)
{
/* initialize the length of the vector */
#ifdef ___ORIGINAL_CODE___
int len = f64vector_length (f64vec);
#else
int len = ___INT(___F64VECTORLENGTH(f64vec));
#endif
/* initializes the new vector */
double * newCvec = malloc (len*sizeof (double));
/* declare a counter */
int i;
/* iterate over the length of the vector copying each object into
the new vector */
for (i = 0; i<len; i++)
{
/* could be re-written using pointer arithmetic */
#ifdef ___ORIGINAL_CODE___
newCvec[i] = f64vector_ref (f64vec, i);
#else
newCvec[i] = ___F64VECTORREF(f64vec, ___FIX(i));
#endif
}
return newCvec;
}
end-of-c-declare
)
;;;;;;;; SOLUTION 2
(define foo
(c-lambda (scheme-object)
(pointer double) #<<end-of-code
{
/* initialize the length of the vector */
int len = f64vector_length (___arg1);
/* initializes the new vector */
double * newCvec = malloc (len*sizeof (double));
/* declare a counter */
int i;
/* iterate over the length of the vector copying each object into
the new vector */
for (i = 0; i<len; i++)
{
/* could be re-written using pointer arithmetic */
newCvec[i] = f64vector_ref (___arg1, i);
}
___result_voidstar = newCvec;
}
end-of-code
))
More information about the Gambit-list
mailing list