Initialization and cleanup code for foreign libaries

Suppose a c library has two functions to initialize the library and to perform clean up before program exit. How do I integrate those two function into haskell using ffi? The initialize function should be called before calling any other functions in the library and the cleanup function should be call right before the program exit.

1 Like

I think the only way is to just make two IO actions for the initialization and termination. That is how the GLFW-b package does it. It has init and terminate.

1 Like

Actually I think foreignptr and unsafeperformio could do this.
Suppose the initialize function return a null pointer, you can use this with unsafeperformio to create a global variable that will be reference by all foreign call to the c library. The clean up function will be called
when no more foreign call to the c library will be made.
I don’t know how correct I’m and this method seem hacky. That’s why I came here to ask if anybody have a better solution.

1 Like

That sounds like it might work. One problem might be that top-level bindings are never garbage collected, so don’t let your termination mechanism depend on that.

1 Like

Definitely hacky for no good reason. Explicit init/terminate is way better and allows more scope control, that can be handy in e.g. tests.

I see bracket was not mentioned. Is it not a fitting solution to this problem?

The problem with the init terminate pair that bracket solves is that an exception may occur between them — bracket makes sure that terminate is still called.

I am not experienced with foreign IO calls. Is there some bit of knowledge that I am missing?

Hey, I found a way to automatically call the clean up function, at least on posix compliant system. You can register a function to be invoke when the program exit with the atexit function.
I’ve just tested this with haskell ffi.

There’s (AFAIK) no way to guarantee some function is executed at the end of some program: for example, a SIGKILL from the outside or call to _exit(2) could cause this.

So, it’s kinda weird for a library to have a “cleanup” function that must be called. What would such function do that isn’t covered by the program simply exiting? Is it meant to clean up things while the process keeps on running, but the library won’t be used anymore? In said case, atexit or similar doesn’t really help, you could as well not call the function.

Using bracket as suggested by @kindaro earlier would be the right approach, if indeed the cleanup function must be called in a “best effort” way:

import Control.Exception.Base (bracket_)

foreign import capi safe "mylibrary.h initMyLibrary"
  initMyLibrary :: IO ()
foreign import capi safe "mylibrary.h cleanupMyLibrary"
  cleanupMyLibrary :: IO ()

withMyLibrary :: IO a -> IO a
withMyLibrary = bracket_ initMyLibrary cleanupMyLibrary

main :: IO ()
main = withMyLibrary $ do
  codeUsingMyLibrary

Of course, in this case, one could all codeUsingMyLibrary in some code where initMyLibrary wasn’t called. There are ways around this, at the cost of API complexity and developer experience, so it’s most of the time not worth the hassle.

1 Like

What if we make withMyLibrary have type ReaderT FancyToken IO α → IO α instead? Here, FancyToken is a type of which no values can be constructed by the user, and all library functions that must be run inside of a library bracket require it. So, there is no way to run library functions outside of a library block.

This is only a slight complication for the user — they need to add liftIO whenever they use standard IO functions inside a library block. At the same time, it reduces cognitive effort, because the user does not need to worry whether library functions are called inside a library bracket — mistakes will be pointed out by the compiler.