I'm on 12.4 (Monterey)
The code I've been using for the gloss/reflex binding, was originally based on the reflex-basic-host package, but I modified that to use reflex's own runHeadlessApp which, as the author of the reflex-basic-host package points out, is basically equivalent.
I've copied the relatively short network running function below.
As can be seen, it runs the gloss playIO function in a separate thread.
If I compile without -threaded, then this works fine. However, it is recommended to compile -threaded to get decent frame rates.
If I do so, and especially if I compile with "-threaded -rtsopts -with-rtsopts=-N -Wall" then I will always get this NSInternalConsistency error.
Presumably, this works without -threaded because Haskell's lightweight thread' will always run on the main OS thread. Whereas all other scenarios will have some chance of being scheduled on a thread other than the main thread, and/or suffering from not being run consistently on the same OS thread (I've read that OpenGL uses thread local state, ergo bound threads are necessary).
I have tried:
- Forking with forkOn 0 (in an attempt to get the main thread - but I don't think just saying 'capability 0' implies the main thread)
- Forking with forkOS (but that will definitely schedule on a new OS thread and not the main thread, even if it is bound to that specific OS thread)
- Using GLFW instead of GLUT. This seemed to make things worse. No more NSInternalConsistency error for sure, but now the it locks up after a frame or two. I've read that this could be the Mac switching between GPUs and not handling the context gracefully, but who knows why GLFW would be more sensitive to that than GLUT (which at least works well in the single-threaded case, whichever GPU it is apparently working on).
I don't know of a way to fork a new Haskell 'fibre' and force it to be scheduled on the main thread when compiled with -threaded. Even if you could do this, I suppose there may be contention between blocking/foreign IO in runHeadlessApp and in playIO anyway.
Here's the code I'm using to run the reflex network for gloss currently:
playReflex display color frequency network =
runHeadlessApp $ do
picTVar <- liftIO $ newTVarIO blank
quitTVar <- liftIO $ newTVarIO False
(tickEvent, tickTrigger) <- newTriggerEvent
(inputEvent, inputTrigger) <- newTriggerEvent
(hostQuitEvent, hostQuitTrigger) <- newTriggerEvent
(dPicture, eQuit) <- network tickEvent (fan $ glossEventMap <$> inputEvent)
performEvent_ $
liftIO . atomically . writeTVar picTVar <$>
updated dPicture
performEvent_ $
(liftIO . atomically $ writeTVar quitTVar True) <$ eQuit
void . liftIO .
flip forkFinally (\_ -> hostQuitTrigger ()) $
playIO
display
color
frequency
()
(\_ -> readTVarIO picTVar)
(\ge _ -> inputTrigger ge)
(\fl _ -> do
shouldQuit <- readTVarIO quitTVar
if shouldQuit
then exitSuccess
else tickTrigger fl)
pure hostQuitEvent