Verbose Logging

software development with some really amazing hair

T + G I F R

Gtk And Ruby Threading Issues

· · Posted in Programming
Tagged with

In one of my classes, we used Ruby and Gtk, but some issues popped up. The most obvious is using a block to do GUI update stuff and the like, from another thread. Things die. Puppies are killed.

I found this post on Ruby Forum which fixed the problem.

Relevant code.

require "thread"
module Gtk
# Thread-safety stuff.
# Loosely based on booh, by Guillaume Cottenceau.
PENDING_CALLS_MUTEX = Mutex.new
PENDING_CALLS = []
def self.thread_protect(&proc)
if Thread.current == Thread.main
proc.call
else
PENDING_CALLS_MUTEX.synchronize do
PENDING_CALLS << proc
end
end
end
def self.thread_flush
if PENDING_CALLS_MUTEX.try_lock
for closure in PENDING_CALLS
closure.call
end
PENDING_CALLS.clear
PENDING_CALLS_MUTEX.unlock
end
end
def self.init_thread_protect
Gtk.timeout_add(100) do
PENDING_CALLS_MUTEX.synchronize do
for closure in PENDING_CALLS
closure.call
end
PENDING_CALLS.clear
end
true
end
end
end
view raw gtk.rb hosted with ❤ by GitHub

Basically, you call the Gtk.init_thread_protect method first when you start things up, then, whenever you need to do GUI update stuff, just wrap it in a Gtk.thread_protect {} block. Voila! It works. No more crashes. In looking at this code again now, some things could be made more Rubyesque, but we'll go with it.

My version with minor changes:

require "thread"
module Gtk
PENDING_CALLS_MUTEX = Mutex.new
PENDING_CALLS = []
def self.thread_protect(&proc)
if Thread.current == Thread.main
proc.call
else
PENDING_CALLS_MUTEX.synchronize do
PENDING_CALLS << proc
end
end
end
def self.thread_flush
if PENDING_CALLS_MUTEX.try_lock
PENDING_CALLS.each { |closure| closure.call }
PENDING_CALLS.clear
PENDING_CALLS_MUTEX.unlock
end
end
def self.init_thread_protect
Gtk.timeout_add(100) do
PENDING_CALLS_MUTEX.synchronize do
PENDING_CALLS.each { |closure| closure.call }
PENDING_CALLS.clear
end
true
end
end
end
view raw gtk-mod.rb hosted with ❤ by GitHub