About GtkThread.sync, GtkThread.async and where do the GUI events live

"Stalkern 2 (Fastweb)" <[email protected]> Thu, 05 Feb 2009 11:46:10 +0100
Newsgroups gmane.comp.lang.ocaml.lib.gtk
Message-ID <[email protected]>
Hello to everybody!

I'm about to work with lablgtk-2.10.1 on a threaded application with
some GUI issues and I want to figure out correctly how does the GUI
manage the GUI events.

So, I've written a test program with two lasting threads and one
short-lived, that affects the materials for the others:

------------------------------------------------------------
buttonConfusedByThreads_v0.1.ml
------------------------------------------------------------
(**************************************************************************)
(*    After the buttons.ml example packaged with lablgtk-2.10.1 *)
(* In this sample code three threads attempt to (conf)use an item that
was born in the very GUI thread.
 * Will the GUI thread understand all the GUI events going on a GUI Item,
 * just because the GUI Item itself is shared between threads?
 *)
(*
 * launch directly with
 * $ lablgtk2 -thread buttonConfusedByThreads_v0.1.ml
 * or compile with
 * $ ocamlfind ocamlopt -thread -package "lablgtk2 unix threads"
-predicates "init threads" -linkpkg -I +lablgtk2 gtkThread.cmx -o
buttonConfusedByThreads_v0.1.opt buttonConfusedByThreads_v0.1.ml
*)

let prepare_gui () =
  (
    let window = GWindow.window ~title:"F.U.D." ~border_width:10 () in
    let _id = window#connect#destroy ~callback:GtkMain.Main.quit in
    let hbox = GPack.hbox ~packing:window#add () in
    let button = GButton.button ~label:"?" ~packing:(hbox#pack
~padding:5) () in
    let _id = button#connect#clicked ~callback:(fun () -> print_endline
(button#label)) in
      (*go!*)
    let () = window#show ()in
      (*pass items over to the confusing threads*)
      (hbox, button)
  )
;;


let () =
  (
    let () = ignore (GtkMain.Main.init ()) (*set locale and the like*) in

    (*start the main loop as a thread*)
    let guiThread = GtkThread.start () in

    (*We should now be inside the GUI Thread. Let's create some GUI*)
    let hb,b = prepare_gui () in

    let _confuserThread1 =
      Thread.create
	(
	  fun () ->
	    while true do
	      (
		let () = print_endline ("YES!") in
		let _testquery = b#label in
		  (b#set_label "YES!")
	      )
	    done
	)
	()
    in

    let _confuserThread2 =
      Thread.create
	(
	  fun () ->
	    while true do
	      (
		let () = print_endline ("NO!") in
		let _testquery = b#label in
		  (b#set_label "NO!")
	      )
	    done
	)
	()
    in

    let _confuserThread3 =
      Thread.create
	(
	  fun () ->
	    let () =
	      for i = 0 to 400 do
		let () = print_endline ("MAYBE #"^(string_of_int i)) in
		(b#set_label "MAYBE!")
	      done
	    in
	      (*attempt to be evil*)
	      (
		List.iter
		 (hb#remove)
		 (hb#all_children)
	      )
	)
	()
    in

    let () =
      Thread.join guiThread
    in
      ()
  )
;;

(*EOF*)
---------------------------------------------------------------------------

And I would expect to change the code like this to have all threads
refer to the main GUI thread loop for the GUI actions:

---------------------------------------------------------------------------
46,47c46,47
<               let _testquery = b#label in
<                 (b#set_label "YES!")
---
>               let _testquery = GtkThread.sync(*we need an answer*)
(fun () -> b#label) () in
>                 (GtkThread.async (fun () -> b#set_label "YES!") () )
61,62c61,62
<               let _testquery = b#label in
<                 (b#set_label "NO!")
---
>               let _testquery = GtkThread.sync(*we need an answer*)
(fun () -> b#label) () in
>                 (GtkThread.async (fun () -> b#set_label "NO!") () )
76c76
<               (b#set_label "MAYBE!")
---
>               (GtkThread.async (fun () -> b#set_label "MAYBE!") () )
81,83c81,90
<               List.iter
<                (hb#remove)
<                (hb#all_children)
---
>               GtkThread.async
>                (
>                  fun () ->
>                    (
>                      List.iter
>                       (hb#remove)
>                       (hb#all_children)
>                    )
>                )
>                ()
96a104
>
---------------------------------------------------------------------------

Do I understand things correctly?
I think that I understand that the management of events on the button is
apart from the button itself, so sharing the button is not enough for
getting a well-designed system of threads.

BTW I am a bit puzzled by the possibility for the (surviving) threads of
requesting the label of a button that has been removed from its
container. Does that button then still exist (albeit unpacked), or not?
Here I hoped to get a plain segfault, actually.


Thank you for your advice.

Ernesto