Using threads in Gdk and Gtk programs

Iñaki edited this page Apr 2, 2017 · 1 revision

There are a couple of important things to keep in mind when using threads in a program using haskell-gi generated bindings, and more specifically gi-gtk.

Gdk and Gtk+ are not multi-thread safe

As described in the GDK documentation the GDK library (and by extension Gtk+) is not multi-thread safe, but there is an easy workaround.

If you want to schedule a GUI operation from a thread which is not the main one, you can use threadsAddIdle as follows

import Control.Monad (void)

import qualified GI.Gdk as Gdk
import qualified GI.GLib as GLib

updateGUI :: IO ()
updateGUI = putStrLn "Running in the main thread"

inSomeThread :: IO ()
inSomeThread = void $
  Gdk.threadsAddIdle GLib.PRIORITY_DEFAULT $ do
    updateGUI
    -- Remove the source from the main loop, so this is not invoked
    -- again.
    return False

Make sure you link against the threaded runtime

It is often convenient to fork a thread in response to some event, in order to perform some expensive computation without blocking the UI. Something like the following, for example:

{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-}

import Control.Concurrent (forkIO)
import Control.Monad (void)

import qualified GI.Gtk as Gtk
import Data.GI.Base

main :: IO ()
main = do
  _ <- Gtk.init Nothing

  myButton <- new Gtk.Button [#label := "Hi there"]
  win <- new Gtk.Window [#child := myButton]

  on win #destroy Gtk.mainQuit

  myButton `on` #clicked $ do
    putStrLn "Test1"
    void $ forkIO (putStrLn "Test2")

  #showAll win

  Gtk.main

If you compile this code

$ ghc --make forkIO.hs

and run it, you may find that when clicking the button only "Test1" appears the first time you click, and the printing of "Test2" is delayed until you click the button again. This is due to the way the GHC handles reentrancy of Haskell code called from C, as explained in some detail here.

To fix this, simply make sure to link with the threaded runtime, by passing the -threaded option to GHC. For example, you could compile the above with

$ ghc --make -threaded forkIO.hs

or if you are using cabal, make sure that the following line is present in your .cabal file:

executable forkIO
   ghc-options: -threaded
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.