I have a C thread which is running in the background, and it makes calls to Haskell code periodically (for logging). Is there a way for the C code to check whether the RTS has started to shut down? I don’t want to crash if the C thread is still logging while the Haskell program is exiting. And I would prefer to do this without having to wrap the Haskell program in bracket or similar.
I asked ChatGPT, and it hallucinated the existence of a function hs_set_exit_hook() in GHC 9.2.1 and later. However, as near as I can tell, this function does not exist in any version of GHC, and has never been mentioned anywhere outside of an AI fever dream.
who is shutting down the haskell RTS? the same thread responsible for calling hs_init should call hs_exit, and set some way to signal to other c threads that they can’t call haskell functions.
The scenario is that I’m writing a Haskell library which wraps an existing C library. It’s possible to pass callback functions to the C library, which in turn would then call back into Haskell.
Most likely, there would just be a Haskell main and not a C main(), so hs_init() and hs_exit() would be called by whatever main() the compiler generates automatically. If there is an explicit C main(), it would not be under my control.
When I said I didn’t want to wrap the program in bracket, what I meant more generally is that I don’t want to require a user of the library to do something special in main, whether that’s a Haskell main or a C main().
is the c thread spawned directly from haskell? is the FunPtr Allocated by the user or the library? assuming your library exposes a (IO a) function, and the FunPtr is allocated by your library just keep count of every funPtr Allocated and wait until the C thread exits to deallocate everything and exit, ie forever $ wait5minforCThread fptrIORef …
if the user has control of when to allocate/free the FunPtr it is probably impossible to not require that they do some special logic in main.
although i think it is possible to escape IORef/MVars/TVars you will then most likely need to use the ContT monad transformer to pretend that callbacks return the value
I do not understand exactly what you are trying to achieve. I am assuming you have some function startBackgroundThreadInC :: (SomeType -> IO ()) -> IO () which takes a Haskell callback and runs in the background for some time.
This is problematic whether your background task runs in Haskell or in C. You need to make sure that your main thread waits for your background tasks to finish, either by waiting or killing them.
In your case, the with pattern seems to me like the obvious solution:
withCThread :: (SomeType -> IO ()) -> IO a -> IO a
withCThread callback program = bracket (startBackgroundThreadInC callback) waitCThread (const program)
The use of bracket is hidden, though I am not sure if this is the interface you want.
If you have multiple functions which interact with the C library in such a callback manner, then you can also try to generalize, ensuring that the C library properly shuts down:
data CLibraryLifetime = CLibraryLifetime
withCThread :: (CLibraryLifetime -> IO a) -> IO a
withCThread program = bracket setupCLibrary waitCThread (const $ program CLibraryLifetime)
startBackgroundThreadInC :: CLibraryLifetime -> (SomeType -> IO ()) -> IO ()
startBackgroundThreadInC _ callback = ...
I like that with-style function that controls the library’s init/shutdown lifecycle, especially since you can make it exception-safe. You can also do a runST-style trick to ensure that library-provided resources cannot escape the with.