How to ensure code executes in the context of a specific OS thread?

Hello, I'm trying to get some GUI code working on OSX and numerous forums around the internet keep reiterating that on OSX to correctly handle GUI events you need to use the original thread allocated to your process to check for events and to call the Cocoa framework functionality. Specifically, using a secondary thread (even a bound thread) is not sufficient with the Cocoa framework. I looked at the threading documentation in Control.Concurrent for GHC and it's not clear to me if this is even possible with GHC without restricting to the non-threaded RTS. This means that using the GUI library from GHCI is not an option and using multiple OS threads in the final application is also not possible. This means that some FFI libraries will be unusable. My main question is, is there a way around this so that I could, for example, use the library from GHCI? My second question is, if there is no current workaround then how can we remedy this situation? It seems like there could be an api function like: runOnOriginalThread :: IO a -> IO a This function would be similar to runInBoundThread except it would be guaranteed to run on the original thread allocated to the ghc/ghci process. I believe the above primitive would be sufficient. I'll worry about filing a bug or making a libraries proposal after I have a better understanding of what must be done. Thanks, Jason

On Sun, Jul 3, 2011 at 10:02 PM, Jason Dagit
Hello,
I'm trying to get some GUI code working on OSX and numerous forums around the internet keep reiterating that on OSX to correctly handle GUI events you need to use the original thread allocated to your process to check for events and to call the Cocoa framework functionality. Specifically, using a secondary thread (even a bound thread) is not sufficient with the Cocoa framework.
Context for others: People explaining that you can't use the secondary thread for GUI operations: http://www.cocoabuilder.com/archive/cocoa/298918-multithread-window-event-ru... http://old.nabble.com/Weird-Carbon%3A-gestalt%3A-wxPython-issue-bug-td274568... http://www.cocoabuilder.com/archive/cocoa/205803-event-loop-in-secondary-thr... http://forums.libsdl.org/viewtopic.php?t=6281&sid=ec818336d68f9797090719b3c5916c21 http://www.cocoabuilder.com/archive/cocoa/292830-nstimer-not-working-in-mult... http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/Multi... http://www.cocoabuilder.com/archive/cocoa/152947-cocoa-multithreading-in-ter... Cocoa provides several ways to run things on the "main thread" (same as I meant when I said original thread): http://blog.jayway.com/2010/03/30/performing-any-selector-on-the-main-thread... http://www.noodlesoft.com/blog/2007/05/01/productive-waste-of-time-figuring-... Jason

On Mon, Jul 4, 2011 at 2:02 AM, Jason Dagit
My second question is, if there is no current workaround then how can we remedy this situation? It seems like there could be an api function like: runOnOriginalThread :: IO a -> IO a
Isn't there something on Cocoa that would allow you to implement this function? For example, to implement postGUISync [1] on Gtk2Hs, Glib's g_idle_add() [2] is used [3]. To implement Gtk.Application.Invoke [4] on Gtk#, Glib's g_timeout_add with a timeout of 0 seconds is used [6]. In other words, some way of running an arbitrary function inside Cocoa's event loop. Cheers! =) [1] http://hackage.haskell.org/packages/archive/gtk/0.12.0/doc/html/Graphics-UI-... [2] http://developer.gnome.org/glib/2.28/glib-The-Main-Event-Loop.html#g-idle-ad... [3] http://hackage.haskell.org/packages/archive/gtk/0.12.0/doc/html/src/Graphics... [4] (link seems broken) http://www.go-mono.com/docs/monodoc.ashx?link=M%3aGtk.Application.Invoke(Sys...) [5] http://developer.gnome.org/glib/2.28/glib-The-Main-Event-Loop.html#g-timeout... [6] https://github.com/mono/gtk-sharp/blob/master/gtk/Application.cs#L200 -- Felipe.

On Sun, Jul 3, 2011 at 10:22 PM, Felipe Almeida Lessa
On Mon, Jul 4, 2011 at 2:02 AM, Jason Dagit
wrote: My second question is, if there is no current workaround then how can we remedy this situation? It seems like there could be an api function like: runOnOriginalThread :: IO a -> IO a
Isn't there something on Cocoa that would allow you to implement this function? For example, to implement postGUISync [1] on Gtk2Hs, Glib's g_idle_add() [2] is used [3]. To implement Gtk.Application.Invoke [4] on Gtk#, Glib's g_timeout_add with a timeout of 0 seconds is used [6]. In other words, some way of running an arbitrary function inside Cocoa's event loop.
There are a few objective-c specific ways of sending something to the right thread. I was hoping to find something a bit more general and easier to call from Haskell (I believe the way the FFI works I'll have to make a new wrapper for each "thing" because I can't directly call objective-c). I suppose I should try adding wrappers around all the GLFW functions so that it sends the request over to the main thread and see if it solves my problem. The downside is that it would be a lot of wrapper code and it would be nice to have solution that other libraries can use. Thanks for the idea. Jason

On Mon, Jul 4, 2011 at 2:38 AM, Jason Dagit
(I believe the way the FFI works I'll have to make a new wrapper for each "thing" because I can't directly call objective-c).
Probably, yes.
I suppose I should try adding wrappers around all the GLFW functions so that it sends the request over to the main thread and see if it solves my problem. The downside is that it would be a lot of wrapper code and it would be nice to have solution that other libraries can use.
Do you mean, on each function of GLFW's API? Usually it's better to call postGUISync/postGUIAsync alikes with the largest number of function calls possible because (a) of the overhead (especially postGUISync's) and (b) it's safer to assume that postGUIAsyncs can be reordered. That is, if you need to call 'do {foo; bar}', instead of using 'do {fooS; barS}' where these are wrapped functions, call 'postGUISync $ do {foo; bar}'. This solution is general in the sense that you only have to code it once for everything Cocoa-related. Cheers! =) -- Felipe.

On Sun, Jul 3, 2011 at 10:02 PM, Jason Dagit
Hello,
I'm trying to get some GUI code working on OSX and numerous forums around the internet keep reiterating that on OSX to correctly handle GUI events you need to use the original thread allocated to your process to check for events and to call the Cocoa framework functionality. Specifically, using a secondary thread (even a bound thread) is not sufficient with the Cocoa framework.
I looked at the threading documentation in Control.Concurrent for GHC and it's not clear to me if this is even possible with GHC without restricting to the non-threaded RTS. This means that using the GUI library from GHCI is not an option and using multiple OS threads in the final application is also not possible. This means that some FFI libraries will be unusable.
My main question is, is there a way around this so that I could, for example, use the library from GHCI?
My second question is, if there is no current workaround then how can we remedy this situation? It seems like there could be an api function like: runOnOriginalThread :: IO a -> IO a
I've got some code in https://github.com/dpp/LispHaskellIPad that uses an FFI call into ObjC code that invokes a function on the UI thread. In Haskell: runOnMain :: IO () -> IO () runOnMain todo = do func <- funky dispatchFunc func where funky = mkStrCB $ \v -> do todo And in ObjC: void dispatchFunc(void (*fp)(void*)) { // dispatch_async_f(dispatch_get_main_queue(), NULL, fp); id runner = [[PerformOMatic alloc] init]; [runner setFunc:fp]; [runner run]; } And: #import "PerformOMatic.h" @implementation PerformOMatic - (void)run { [self performSelectorOnMainThread:@selector(reallyDoIt:) withObject:self waitUntilDone:TRUE]; } - (void)reallyDoIt:(id)ignore { whatToDo(NULL); releaseMe(whatToDo); [self dealloc]; } - (void)setFunc:(void *)func { whatToDo = func; } @end Pardon the extremely ugly code, I'm a Haskell newbie and my ObjC skills are 20 years old.
This function would be similar to runInBoundThread except it would be guaranteed to run on the original thread allocated to the ghc/ghci process. I believe the above primitive would be sufficient.
I'll worry about filing a bug or making a libraries proposal after I have a better understanding of what must be done.
Thanks, Jason
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Lift, the simply functional web framework http://liftweb.net Simply Lift http://simply.liftweb.net Follow me: http://twitter.com/dpp Blog: http://goodstuff.im

On 04/07/11 06:02, Jason Dagit wrote:
Hello,
I'm trying to get some GUI code working on OSX and numerous forums around the internet keep reiterating that on OSX to correctly handle GUI events you need to use the original thread allocated to your process to check for events and to call the Cocoa framework functionality. Specifically, using a secondary thread (even a bound thread) is not sufficient with the Cocoa framework.
I looked at the threading documentation in Control.Concurrent for GHC and it's not clear to me if this is even possible with GHC without restricting to the non-threaded RTS. This means that using the GUI library from GHCI is not an option and using multiple OS threads in the final application is also not possible. This means that some FFI libraries will be unusable.
In a compiled program, the main thread is a bound thread, bound to the main OS thread of the process (i.e. the GUI thread in your case). So you can safely make Cocoa calls using the main thread of your compiled Haskell program, and from other threads if you add some way to forward operations to the main thread, like gtk2hs's postGUI. In GHCi it's a different matter, because the main thread is running GHCi itself, and all the expressions/statements typed at the prompt are run in forkIO'd threads (a new one for each statement, in fact). If you want a way to run command-line operations in the main thread, please submit a feature request. I'm not sure it can be done, but I'll look into it. Cheers, Simon

On Tue, Jul 05, 2011 at 08:11:21PM +0100, Simon Marlow wrote:
In GHCi it's a different matter, because the main thread is running GHCi itself, and all the expressions/statements typed at the prompt are run in forkIO'd threads (a new one for each statement, in fact). If you want a way to run command-line operations in the main thread, please submit a feature request. I'm not sure it can be done, but I'll look into it.
We already have a way: -fno-ghci-sandbox Thanks Ian

On Tue, Jul 5, 2011 at 12:33 PM, Ian Lynagh
On Tue, Jul 05, 2011 at 08:11:21PM +0100, Simon Marlow wrote:
In GHCi it's a different matter, because the main thread is running GHCi itself, and all the expressions/statements typed at the prompt are run in forkIO'd threads (a new one for each statement, in fact). If you want a way to run command-line operations in the main thread, please submit a feature request. I'm not sure it can be done, but I'll look into it.
We already have a way: -fno-ghci-sandbox
I've removed all my explicit attempts to forkIO/forkOS and passed the command line flag you mention. I just tried this but it doesn't change the behavior in my example. Jason

On 05/07/2011 20:33, Ian Lynagh wrote:
On Tue, Jul 05, 2011 at 08:11:21PM +0100, Simon Marlow wrote:
In GHCi it's a different matter, because the main thread is running GHCi itself, and all the expressions/statements typed at the prompt are run in forkIO'd threads (a new one for each statement, in fact). If you want a way to run command-line operations in the main thread, please submit a feature request. I'm not sure it can be done, but I'll look into it.
We already have a way: -fno-ghci-sandbox
Aha, I'd forgotten about that! Thanks Ian. Simon

On Tue, Jul 5, 2011 at 12:11 PM, Simon Marlow
On 04/07/11 06:02, Jason Dagit wrote:
Hello,
I'm trying to get some GUI code working on OSX and numerous forums around the internet keep reiterating that on OSX to correctly handle GUI events you need to use the original thread allocated to your process to check for events and to call the Cocoa framework functionality. Specifically, using a secondary thread (even a bound thread) is not sufficient with the Cocoa framework.
I looked at the threading documentation in Control.Concurrent for GHC and it's not clear to me if this is even possible with GHC without restricting to the non-threaded RTS. This means that using the GUI library from GHCI is not an option and using multiple OS threads in the final application is also not possible. This means that some FFI libraries will be unusable.
In a compiled program, the main thread is a bound thread, bound to the main OS thread of the process (i.e. the GUI thread in your case). So you can safely make Cocoa calls using the main thread of your compiled Haskell program, and from other threads if you add some way to forward operations to the main thread, like gtk2hs's postGUI.
Is my understanding correct that this is only the case for the non-threaded RTS? If so, what do you do when you need to use the threaded RTS? My test was to check if the main thread was bound when compiling with -threaded. I got the impression that I couldn't guarantee that the code was running on the original thread.
In GHCi it's a different matter, because the main thread is running GHCi itself, and all the expressions/statements typed at the prompt are run in forkIO'd threads (a new one for each statement, in fact). If you want a way to run command-line operations in the main thread, please submit a feature request. I'm not sure it can be done, but I'll look into it.
I'll try Ian's suggestion of -fno-ghci-sandbox when I get a chance. Thanks, Jason

On 05/07/2011 20:38, Jason Dagit wrote:
On Tue, Jul 5, 2011 at 12:11 PM, Simon Marlow
wrote: On 04/07/11 06:02, Jason Dagit wrote:
Hello,
I'm trying to get some GUI code working on OSX and numerous forums around the internet keep reiterating that on OSX to correctly handle GUI events you need to use the original thread allocated to your process to check for events and to call the Cocoa framework functionality. Specifically, using a secondary thread (even a bound thread) is not sufficient with the Cocoa framework.
I looked at the threading documentation in Control.Concurrent for GHC and it's not clear to me if this is even possible with GHC without restricting to the non-threaded RTS. This means that using the GUI library from GHCI is not an option and using multiple OS threads in the final application is also not possible. This means that some FFI libraries will be unusable.
In a compiled program, the main thread is a bound thread, bound to the main OS thread of the process (i.e. the GUI thread in your case). So you can safely make Cocoa calls using the main thread of your compiled Haskell program, and from other threads if you add some way to forward operations to the main thread, like gtk2hs's postGUI.
Is my understanding correct that this is only the case for the non-threaded RTS?
No - I'm talking about the threaded RTS here. It's trivially true of the non-threaded RTS too, because there's only one OS thread.
If so, what do you do when you need to use the threaded RTS? My test was to check if the main thread was bound when compiling with -threaded. I got the impression that I couldn't guarantee that the code was running on the original thread.
You do have that guarantee for the main thread. Could you point out the docs that gave you the opposite impression - I'll see if we can improve them. Cheers, Simon

On Wed, Jul 6, 2011 at 2:20 AM, Simon Marlow
On 05/07/2011 20:38, Jason Dagit wrote:
On Tue, Jul 5, 2011 at 12:11 PM, Simon Marlow
wrote: On 04/07/11 06:02, Jason Dagit wrote:
Hello,
I'm trying to get some GUI code working on OSX and numerous forums around the internet keep reiterating that on OSX to correctly handle GUI events you need to use the original thread allocated to your process to check for events and to call the Cocoa framework functionality. Specifically, using a secondary thread (even a bound thread) is not sufficient with the Cocoa framework.
I looked at the threading documentation in Control.Concurrent for GHC and it's not clear to me if this is even possible with GHC without restricting to the non-threaded RTS. This means that using the GUI library from GHCI is not an option and using multiple OS threads in the final application is also not possible. This means that some FFI libraries will be unusable.
In a compiled program, the main thread is a bound thread, bound to the main OS thread of the process (i.e. the GUI thread in your case). So you can safely make Cocoa calls using the main thread of your compiled Haskell program, and from other threads if you add some way to forward operations to the main thread, like gtk2hs's postGUI.
Is my understanding correct that this is only the case for the non-threaded RTS?
No - I'm talking about the threaded RTS here. It's trivially true of the non-threaded RTS too, because there's only one OS thread.
If so, what do you do when you need to use the threaded RTS? My test was to check if the main thread was bound when compiling with -threaded. I got the impression that I couldn't guarantee that the code was running on the original thread.
You do have that guarantee for the main thread. Could you point out the docs that gave you the opposite impression - I'll see if we can improve them.
I did read this section: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Concurren... And I played with some of the functions there. What I didn't take away from this was the guarantee that main runs on the original OS thread. I had the impression that it was running from some bound thread, possibly created via runInBoundThread. I think that ghc and ghci are quite different in this regard. It would probably be good to mention that distinction. Jason
participants (6)
-
David Pollak
-
Edward Z. Yang
-
Felipe Almeida Lessa
-
Ian Lynagh
-
Jason Dagit
-
Simon Marlow