[racket-dev] occasional seg faults when calling C routine

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Fri Oct 21 17:30:44 EDT 2011

At Fri, 21 Oct 2011 14:10:03 -0700, John Clements wrote:
> I'm getting occasional (non-reliably-reproducible) seg faults when calling a C 
> routine that copies bits from one place to another.
> 
> My working assumption is that the buffer is getting moved by GC in between the 
> computation of the pointers and the actual ffi invocation, though that's a 
> fairly tight window, given that the pointers are computed directly as the 
> arguments to the ffi call. I have some questions to see whether I'm 
> understanding what's going on:
> 
> 1) Is it true that GCs cannot occur during the evaluation of ffi code on the 
> main thread?

True, as long as the foreign code does not call back to Racket or the
Racket run-time system.

> 2) Is there any way either to disable GC briefly or to prevent an object from 
> being moved?

There's not really a way to disable GC. You can allocate atomic memory
with 'atomic-interior, in which case it will not move around.

> 3) lacking this, (and assuming that #1 is true), it looks like the right thing 
> is to use malloc-immobile cell so that the pointer lookup can happen inside of 
> the C code. Does this sound correct?

I'm not sure I understand the problem, but it doesn't sound like a case
when an immobile cell is the solution.

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

In case it helps, I'm thining of adding something like the following to
the docs:


For the following scenarios, assume

 * A `define-mylib' form of the sort that `ffi/unsafe/define' would
   produce.

 * A C function f() that expects a pointer to few bytes that it will
    read/write, and that will be found by `define-mylib'.

 * No calls back to Racket functions or the Racket run-time system by
   f().


1. 
   (define-mylib f (_fun _pointer -> _void))
   (define p (malloc 'atomic 1024))

   (f p)

This is ok. Although the data allocated by `malloc' can move around,
`p' will always point to it, and no GC will happen between the time
that the address is extracted form `p' to pass to f() and when f()
returns.

2. 
   (define-mylib f (_fun _intptr -> _void))
   (define p (malloc 'atomic 1024))
   (define i (cast p _pointer _intptr))

   (f i)

This is bad. The data referenced by `p' can move before f() is called,
in which case `i' will not hold the address of the data.

3. 
   (define-mylib f (_fun _pointer -> _void))
   (define p (malloc 'atomic 1024))
   (define p2 (ptr-add p 4))

   (f p2)

This is ok. The pointer `p2' retains the original address and only adds
the 4 at the last minute before calling f() (i.e., after the point that
GCing is allowed).

4. 
   (define-mylib f (_fun _intptr -> _void))
   (define p (malloc 'atomic-interior 1024))
   (define i (cast p _pointer _intptr))

   (f i)

This is ok, assuming that `p' itself is referenced strongly enough that
the data it references isn't reclaimed. Allocating with
'atomic-interior puts data at a particular address and keeps it there.
A GC will not move `p', and so `i' (interpreted as an address) will
always refer to the same data.



Posted on the dev mailing list.