racket / drracket

DrRacket, IDE for Racket
http://www.racket-lang.org/
Other
444 stars 93 forks source link

`ffi` in drracket won't use new shared library after modification & recompiling #647

Closed shenlebantongying closed 7 months ago

shenlebantongying commented 8 months ago

After press the run button, a library loaded by ffi seems to be staying around. After modifying and recompiling the library, ffi still uses the older library when pressing run.

To reproduce:

Compile this library

// hello.c
// gcc -c -fPIC hello.c && gcc hello.o -shared -o libhello.dylib
char * hello(){
    return "hello";
}

Uses it in drracket.

#lang racket

(require ffi/unsafe
         ffi/unsafe/define)

(define-ffi-definer define-draw (ffi-lib "libhello"))

(define-draw hello (_fun -> _string))
(hello)

Modify the hello.c file, change the return string and recompile

-> Press the run in drracket, it will output the older return string from the older version of the library.

Video to demo this https://imgur.com/a/bYipZXU (quality is pretty horrible but you can see the output string isn't changed in drracket, while the output string indeed changed confirmed by invoking the racket file directly.)

System: macOS 14 Racket/CS 8.10

soegaard commented 8 months ago

Here is what happens in DrRacket:

When you run the program the first time, the shared library is attached to the DrRacket process. When the program is run the second time, the attached library is reused.

It would be nice if there were a way to unload and/or reload a shared library, but as far as I know there is no support for that currently [1].

A practical solution: edit the program in DrRacket, but run it in a nearby terminal.

[1] https://groups.google.com/g/racket-users/c/KRrB8XZKNxA/m/a6SJ7ztHj6UJ

LiberalArtist commented 8 months ago

It would be nice if there were a way to unload and/or reload a shared library, but as far as I know there is no support for that currently [1].

There is (relatively new) support for this through the #:custodian argument to ffi-lib: if you supply (current-custodian), the library will be unloaded, and thus can be reloaded, when DrRacket discards an old REPL and starts a new one.

Using a custodian for unloading the library creates additional requirements referred to in the docs to manage potentially invalid library references. Even if you don't intend to take on those requirements, and thus will ultimately want to supply 'place for #:custodian or leave the default, it can still be useful to supply #:custodian (current-custodian) during early development, when you're trying to get library loading working properly, or if you are actively modifying the foreign code being used.

As a general caveat, though, because the FFI uses unsafe functionality, DrRacket cannot protect itself from users' buggy unsafe programs as well as it can from buggy safe programs. For example, you can easily cause a segfault, corrupt memory, or otherwise crash DrRacket using the FFI. I still find DrRacket useful for FFI development, but it is worth being extra cautious.

shenlebantongying commented 8 months ago

Glad to know what's really going on. This is much deeper than I thought.

I will try the custodian method. If it doesn't work, I will stick with running in a terminal.

shenlebantongying commented 7 months ago

https://docs.racket-lang.org/reference/custodians.html#%28def._%28%28quote._~23~25kernel%29._custodian-shutdown-all%29%29

Related doc exists.

image