[racket-dev] occasional seg faults when calling C routine
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.