Ruby and Tk question

Discussion in 'Ruby' started by Stephen Kellett, Sep 29, 2005.

  1. Hello everyone,

    I've been trying to write a trivial multi-threaded application that has
    a GUI (implemented using TkRuby) and which updates various counters on
    the GUI. Problem is it deadlocks as soon as I try to update the
    graphical part, but I am unsure why. I've included the most stripped
    down version of the code here - which will not deadlock - it will just
    output to stdout. If you comment in the two obvious lines (commented to
    indicate which ones) then it does deadlock.

    Am I doing something wrong?
    Have I made incorrect assumptions?
    How should I do this?
    Is it possible to update Tk components from threads other than the main
    GUI thread?

    Cheers

    Stephen

    require 'tk' # get widget classes
    require 'thread'

    class RTVExample < TkRoot
    def initialize(args)
    super(args)

    @countA = 0
    @countB = 0
    @lock1 = Mutex.new

    # create our UI

    @menubar = TkMenubar.new(nil, nil)
    @menubar.pack('side'=>'top', 'fill'=>'x')

    @menubar.add_menu([['File', 0],
    ['Exit', proc{exitFunc}, 0]])

    @menubar.add_menu([['Thread Test', 0],
    ['Test 1', proc{testThread1}, 0]])

    @counterLabel = TkLabel.new{text 'Counter' ; pack {padx=30 ; pady=0}}
    end

    # why does this deadlock? - surely it should just run the two threads alongside the GUI thread.

    def testThread1()
    # create two the threads - comment in the #@counter lines to see the deadlock

    tok1 = Thread.new do
    10000.times {
    @lock1.synchronize { @countA += 1 }
    puts "A:" + @countA.to_s()

    # this next line causes the deadlock - why?
    #@counterLabel.configure(text=>"Counter A: %d" % @countA)
    }
    end
    tok2 = Thread.new do
    10000.times {
    @lock1.synchronize { @countB += 1 }
    puts "B:" + @countB.to_s()

    # this next line causes the deadlock - why?
    #@counterLabel.configure(text=>"Counter B: %d" % @countB)
    }
    end

    tok1.join
    tok2.join
    end

    def exitFunc()
    TkRoot.destroy()
    end
    end

    # run the GUI app

    gui = RTVExample.new()
    Tk.mainloop()

    --
    Stephen Kellett
    Object Media Limited http://www.objmedia.demon.co.uk/software.html
    Computer Consultancy, Software Development
    Windows C++, Java, Assembler, Performance Analysis, Troubleshooting
     
    Stephen Kellett, Sep 29, 2005
    #1
    1. Advertising

  2. Stephen Kellett

    Daniel Lewis Guest

    Deadlocks are usually to do with the OS. Maybe Tk can't cope with
    threads. Have you tried this in other Graphical Toolkits, WxRuby is
    quite nice and GTK seems quite good.

    On 29/09/05, Stephen Kellett <> wrote:
    > Hello everyone,
    >
    > I've been trying to write a trivial multi-threaded application that has
    > a GUI (implemented using TkRuby) and which updates various counters on
    > the GUI. Problem is it deadlocks as soon as I try to update the
    > graphical part, but I am unsure why. I've included the most stripped
    > down version of the code here - which will not deadlock - it will just
    > output to stdout. If you comment in the two obvious lines (commented to
    > indicate which ones) then it does deadlock.
    >
    > Am I doing something wrong?
    > Have I made incorrect assumptions?
    > How should I do this?
    > Is it possible to update Tk components from threads other than the main
    > GUI thread?
    >
    > Cheers
    >
    > Stephen
    >
    > require 'tk' # get widget classes
    > require 'thread'
    >
    > class RTVExample < TkRoot
    > def initialize(args)
    > super(args)
    >
    > @countA =3D 0
    > @countB =3D 0
    > @lock1 =3D Mutex.new
    >
    > # create our UI
    >
    > @menubar =3D TkMenubar.new(nil, nil)
    > @menubar.pack('side'=3D>'top', 'fill'=3D>'x')
    >
    > @menubar.add_menu([['File', 0],
    > ['Exit', proc{exitFunc}, 0]])
    >
    > @menubar.add_menu([['Thread Test', 0],
    > ['Test 1', proc{testThread1}, 0]])
    >
    > @counterLabel =3D TkLabel.new{text 'Counter' ; pack {padx=3D30 ; pady=

    =3D0}}
    > end
    >
    > # why does this deadlock? - surely it should just run the two threads a=

    longside the GUI thread.
    >
    > def testThread1()
    > # create two the threads - comment in the #@counter lines to see the =

    deadlock
    >
    > tok1 =3D Thread.new do
    > 10000.times {
    > @lock1.synchronize { @countA +=3D 1 }
    > puts "A:" + @countA.to_s()
    >
    > # this next line causes the deadlock - why?
    > #@counterLabel.configure(text=3D>"Counter A: %d" % @c=

    ountA)
    > }
    > end
    > tok2 =3D Thread.new do
    > 10000.times {
    > @lock1.synchronize { @countB +=3D 1 }
    > puts "B:" + @countB.to_s()
    >
    > # this next line causes the deadlock - why?
    > #@counterLabel.configure(text=3D>"Counter B: %d" % @c=

    ountB)
    > }
    > end
    >
    > tok1.join
    > tok2.join
    > end
    >
    > def exitFunc()
    > TkRoot.destroy()
    > end
    > end
    >
    > # run the GUI app
    >
    > gui =3D RTVExample.new()
    > Tk.mainloop()
    >
    > --
    > Stephen Kellett
    > Object Media Limited http://www.objmedia.demon.co.uk/software.html
    > Computer Consultancy, Software Development
    > Windows C++, Java, Assembler, Performance Analysis, Troubleshooting
    >
    >
     
    Daniel Lewis, Sep 29, 2005
    #2
    1. Advertising

  3. In message <>, Daniel Lewis
    <> writes
    >Deadlocks are usually to do with the OS. Maybe Tk can't cope with
    >threads. Have you tried this in other Graphical Toolkits, WxRuby is
    >quite nice and GTK seems quite good.


    I haven't tried other GUI toolkits (I wasn't particularly aware of them
    - the PickAxe book talks about Tk). The equivalent code with Python and
    Tk runs just fine.

    Stephen
    --
    Stephen Kellett
    Object Media Limited http://www.objmedia.demon.co.uk/software.html
    Computer Consultancy, Software Development
    Windows C++, Java, Assembler, Performance Analysis, Troubleshooting
     
    Stephen Kellett, Sep 29, 2005
    #3
  4. From: Stephen Kellett <>
    Subject: Ruby and Tk question
    Date: Thu, 29 Sep 2005 19:41:45 +0900
    Message-ID: <>
    > I've been trying to write a trivial multi-threaded application that has
    > a GUI (implemented using TkRuby) and which updates various counters on
    > the GUI. Problem is it deadlocks as soon as I try to update the
    > graphical part, but I am unsure why. I've included the most stripped
    > down version of the code here - which will not deadlock - it will just
    > output to stdout. If you comment in the two obvious lines (commented to
    > indicate which ones) then it does deadlock.
    >
    > Am I doing something wrong?
    > Have I made incorrect assumptions?
    > How should I do this?
    > Is it possible to update Tk components from threads other than the main
    > GUI thread?


    On Ruby/Tk, all operations and callbacks are evaluated on the
    eventloop thread. All calls are serialized to avoid conflict of
    GUI operation. Therefore, never wait or sleep on the callback
    operation!!

    Your callback "testThread1" waits two threads.
    That is not so serious problem except broken respense of GUI.
    However, each of the threads which "testThread1" waits calls
    a Tk operation "@counterLabel.configure".
    The Tk operation sends a request to the eventloop and waits
    for the return value from the eventloop.
    It makes deadlock!.

    If "testThread1" doen't the created threads, there is no problem
    except your typo ;-)
    > #@counterLabel.configure(text=>"Counter A: %d" % @countA)

    ^^^^
    > #@counterLabel.configure(text=>"Counter B: %d" % @countB)

    ^^^^
    :text or "text"

    However, I think, your request is "disable the menu entry between
    callback operations are working".
    If that is true, Please read the rewrited example at the end of
    this mail.

    # BTW, TkTimer class may help you sometimes. For example,
    # -------------------------------------------------------------
    # TkTimer.new(0, 1000){ <operations of each call> }.start
    # -------------------------------------------------------------
    # This calls the block 1000 times with 0ms (or minimum) interval.
    # TkTimer works on the eventloop. So, there is no thread-switching.

    ---------< example script >--------------------------------------
    require 'tk' # get widget classes
    require 'thread'

    class RTVExample < TkRoot
    def initialize(args)
    super(args)

    @countA = 0
    @countB = 0
    @lock1 = Mutex.new

    # create our UI

    @menubar = TkMenubar.new(nil, nil)
    @menubar.pack('side'=>'top', 'fill'=>'x')

    @menubar.add_menu([['File', 0],
    ['Exit', proc{exitFunc}, 0]])

    @th_test_mbtn, @th_test_menu =
    @menubar.add_menu([['Thread Test', 0],
    ['Test 1', proc{testThread1}, 0]])

    @counterLabel = TkLabel.new{text 'Counter' ; pack {padx=30 ; pady=0}}
    end

    # why does this deadlock? - surely it should just run the two threads alongside the GUI thread.

    def testThread1()
    # create two the threads - comment in the #@counter lines to see the deadlock
    Thread.new do
    begin
    @th_test_menu.entryconfigure('Test 1', :state=>:disabled)

    tok1 = Thread.new do
    10000.times {
    @lock1.synchronize { @countA += 1 }
    puts "A:" + @countA.to_s()

    # this next line causes the deadlock - why?
    @counterLabel.configure:)text=>"Counter A: %d" % @countA)
    Tk.update_idletasks
    }
    end
    tok2 = Thread.new do
    10000.times {
    @lock1.synchronize { @countB += 1 }
    puts "B:" + @countB.to_s()

    # this next line causes the deadlock - why?
    @counterLabel.configure:)text=>"Counter B: %d" % @countB)
    Tk.update_idletasks
    }
    end

    begin
    tok1.join
    rescue
    # ignore error on Thread
    end
    begin
    tok2.join
    rescue
    # ignore error on Thread
    end
    ensure
    @th_test_menu.entryconfigure('Test 1', :state=>:normal)
    end
    end
    end

    def exitFunc()
    TkRoot.destroy()
    end
    end

    # run the GUI app

    gui = RTVExample.new()
    Tk.mainloop()
    -----------------------------------------------------------------
    --
    Hidetoshi NAGAI ()


    >
    > Cheers
    >
    > Stephen
    >
    > require 'tk' # get widget classes
    > require 'thread'
    >
    > class RTVExample < TkRoot
    > def initialize(args)
    > super(args)
    >
    > @countA = 0
    > @countB = 0
    > @lock1 = Mutex.new
    >
    > # create our UI
    >
    > @menubar = TkMenubar.new(nil, nil)
    > @menubar.pack('side'=>'top', 'fill'=>'x')
    >
    > @menubar.add_menu([['File', 0],
    > ['Exit', proc{exitFunc}, 0]])
    >
    > @menubar.add_menu([['Thread Test', 0],
    > ['Test 1', proc{testThread1}, 0]])
    >
    > @counterLabel = TkLabel.new{text 'Counter' ; pack {padx=30 ; pady=0}}
    > end
    >
    > # why does this deadlock? - surely it should just run the two threads alongside the GUI thread.
    >
    > def testThread1()
    > # create two the threads - comment in the #@counter lines to see the deadlock
    >
    > tok1 = Thread.new do
    > 10000.times {
    > @lock1.synchronize { @countA += 1 }
    > puts "A:" + @countA.to_s()
    >
    > # this next line causes the deadlock - why?
    > #@counterLabel.configure(text=>"Counter A: %d" % @countA)
    > }
    > end
    > tok2 = Thread.new do
    > 10000.times {
    > @lock1.synchronize { @countB += 1 }
    > puts "B:" + @countB.to_s()
    >
    > # this next line causes the deadlock - why?
    > #@counterLabel.configure(text=>"Counter B: %d" % @countB)
    > }
    > end
    >
    > tok1.join
    > tok2.join
    > end
    >
    > def exitFunc()
    > TkRoot.destroy()
    > end
    > end
    >
    > # run the GUI app
    >
    > gui = RTVExample.new()
    > Tk.mainloop()
    >
    > --
    > Stephen Kellett
    > Object Media Limited http://www.objmedia.demon.co.uk/software.html
    > Computer Consultancy, Software Development
    > Windows C++, Java, Assembler, Performance Analysis, Troubleshooting
    >
     
    Hidetoshi NAGAI, Sep 30, 2005
    #4
  5. In message <>, Hidetoshi
    NAGAI <> writes

    Thank you for answer. Most helpful.

    Stephen

    >On Ruby/Tk, all operations and callbacks are evaluated on the
    >eventloop thread. All calls are serialized to avoid conflict of
    >GUI operation. Therefore, never wait or sleep on the callback
    >operation!!


    ....
    --
    Stephen Kellett
    Object Media Limited http://www.objmedia.demon.co.uk/software.html
    Computer Consultancy, Software Development
    Windows C++, Java, Assembler, Performance Analysis, Troubleshooting
     
    Stephen Kellett, Sep 30, 2005
    #5
  6. Stephen Kellett

    x1 Guest

    Awesome Hidetoshi! Thanks

    On 9/30/05, Stephen Kellett <> wrote:
    > In message <>, Hidetoshi
    > NAGAI <> writes
    >
    > Thank you for answer. Most helpful.
    >
    > Stephen
    >
    > >On Ruby/Tk, all operations and callbacks are evaluated on the
    > >eventloop thread. All calls are serialized to avoid conflict of
    > >GUI operation. Therefore, never wait or sleep on the callback
    > >operation!!

    >
    > ....
    > --
    > Stephen Kellett
    > Object Media Limited http://www.objmedia.demon.co.uk/software.html
    > Computer Consultancy, Software Development
    > Windows C++, Java, Assembler, Performance Analysis, Troubleshooting
    >
    >
     
    x1, Oct 1, 2005
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    0
    Views:
    222
  2. anne001
    Replies:
    1
    Views:
    526
  3. Phrogz
    Replies:
    4
    Views:
    244
    Austin Ziegler
    Sep 6, 2006
  4. roschler
    Replies:
    0
    Views:
    189
    roschler
    Oct 16, 2006
  5. Nicholas
    Replies:
    3
    Views:
    397
    Ryan Davis
    Jan 28, 2007
Loading...

Share This Page