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@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 ))