At Mon, 17 Sep 2018 09:34:46 -0700 (PDT), Stephen Foster wrote:
> But when I have two bodies that share the same velocity_func, I get a 
> segfault:

The problem is that a callback is retained (by default) as long as the
function that it wraps is retained, but only a single callback is
retained for a given function. So, creating a second callback loses the
link between the function and the first callback, and the first
callback can be is reclaimed by the GC.

This is often a difficult problem to solve.

> *Things that didn't work*
> 
> 1) Wrapping the function f in separate lambdas still gives a segfault:
> 
> (set-cpBody-velocity_func! (chipmunk-body body1)
>                            (lambda(b g d dt)
>                              (f b d g dt)))
> 
> (set-cpBody-velocity_func! (chipmunk-body body2)
>                            (lambda(b g d dt)
>                              (f b d g dt)))

This change makes things slightly worse, because nothing retains either
of those individual lambdas.

> 2) Storing a reference to the function f in a stab-in-the-dark attempt to 
> "trick the system" or "prevent some kind of mysterious garbage collection" 
> still segfaults:
> 
> (define probably-silly
>   (list
>     (lambda(b g d dt)
>       (f b d g dt))
>     (lambda(b g d dt)
>       (f b d g dt))))
> 
> (set-cpBody-velocity_func! (chipmunk-body body1)
>                            (list-ref probably-silly 0))
> 
> (set-cpBody-velocity_func! (chipmunk-body body2)
>                            (list-ref probably-silly 1))

Honestly, I'm not clear on why this fails. The `probably-silly` list
should be retained via the module's namespace, and so both of those
`lambda`s should be preserved, and so both callbacks should be
preserved. I'm stumped.


> 3) Storing the function f in two separate variables also segfaults:
> 
> (define (f body gravity damping dt)
>   (ffi:cpointer-push-tag! body 'cpBody)
>   (cpBodySetVelocity body (cpv 0 -10))
>     
>   ffi:_void)
> 
> (define f2 f)

This does nothing, since `f` and `f2` refer to the same value.


> *Things that do work but feel dumb*
> 
> 1) Explicitly duplicating the function verbatim:
> 
> (define (f body gravity damping dt)
>   (ffi:cpointer-push-tag! body 'cpBody)
>   (cpBodySetVelocity body (cpv 0 -10))
>     
>   ffi:_void)
> 
> (define (f2 body gravity damping dt)
>   (ffi:cpointer-push-tag! body 'cpBody)
>   (cpBodySetVelocity body (cpv 0 -10))
>     
>   ffi:_void)
> 
> (set-cpBody-velocity_func! (chipmunk-body body1)
>                            f)
> 
> (set-cpBody-velocity_func! (chipmunk-body body2)
>                            f2)

This works because each callbacks is associated to a separate function,
and the separate functions are retained through the module's namespace.

> 2) Duplicating the function with a macro lets me generalize the above 
> workaround for more than two bodies, but it still feels gross.

Yes.


> If someone has an explanation or a better workaround, that would be great.  

Use `#:keep` in the `_fun` for the velocity callback.

To tie the callback's lifetime to the enclosing record, you can
probably use a weak hashtable mapping `cpBody` pointers to callbacks:

  (define callbacks (make-weak-hasheq))
  (define current-cpBody (make-parameter #f))

  (define _cpBodyVelocityFunc
     (_fun #:keep (lambda (cb) (hash-set! callbacks (current-cpBody) cb))
           _pointer _cpVect _cpFloat _cpFloat -> _void))
  
where `current-cpBody` is a way of communicating the record where the
pointer is being stored:

  (let ([body (chipmunk-body body1)])
    (parameterize ([current-cpBody body]))
      (set-cpBody-velocity_func! body
                                  f))

Beware that `callbacks` ties the callback to a cpointer record, not to
the actual pointer value. Since `chipmunk-body` returns the same
Cpointer every time for a given `chipmunk` Racket structure, I think
that's ok --- but it won't be ok if you get the same body pointer from
Chipmunk (via the FFI) multiple times.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to