From 67d97f9f54921affa84108311b4b1ca659b051a9 Mon Sep 17 00:00:00 2001 From: Guillaume Cottenceau Date: Tue, 16 Dec 2008 23:44:49 +0100 Subject: [PATCH] better synchronization and threads use --- bin/booh | 236 +++++++++++++++++++++++-------------- lib/booh/Synchronizator.rb | 54 --------- lib/booh/booh-lib.rb | 41 ++++--- 3 files changed, 174 insertions(+), 157 deletions(-) delete mode 100644 lib/booh/Synchronizator.rb diff --git a/bin/booh b/bin/booh index 980c456..5a3a8db 100755 --- a/bin/booh +++ b/bin/booh @@ -37,7 +37,6 @@ include REXML require 'booh/booh-lib' include Booh require 'booh/UndoHandler' -require 'booh/Synchronizator' #- options @@ -49,7 +48,6 @@ $options = [ #- default values for some globals $xmldir = nil -$xmlaccesslock = Object.new $modified = false $current_cursor = nil $ignore_videos = false @@ -518,33 +516,41 @@ end def color_swap(xmldir, attributes_prefix) $modified = true - if xmldir.attributes["#{attributes_prefix}color-swap"] - xmldir.delete_attribute("#{attributes_prefix}color-swap") - else - xmldir.add_attribute("#{attributes_prefix}color-swap", '1') - end + rexml_thread_protect { + if xmldir.attributes["#{attributes_prefix}color-swap"] + xmldir.delete_attribute("#{attributes_prefix}color-swap") + else + xmldir.add_attribute("#{attributes_prefix}color-swap", '1') + end + } end def enhance(xmldir, attributes_prefix) $modified = true - if xmldir.attributes["#{attributes_prefix}enhance"] - xmldir.delete_attribute("#{attributes_prefix}enhance") - else - xmldir.add_attribute("#{attributes_prefix}enhance", '1') - end + rexml_thread_protect { + if xmldir.attributes["#{attributes_prefix}enhance"] + xmldir.delete_attribute("#{attributes_prefix}enhance") + else + xmldir.add_attribute("#{attributes_prefix}enhance", '1') + end + } end def change_seektime(xmldir, attributes_prefix, value) $modified = true - xmldir.add_attribute("#{attributes_prefix}seektime", value) + rexml_thread_protect { + xmldir.add_attribute("#{attributes_prefix}seektime", value) + } end def ask_new_seektime(xmldir, attributes_prefix) - if xmldir - value = xmldir.attributes["#{attributes_prefix}seektime"] - else - value = '' - end + rexml_thread_protect { + if xmldir + value = xmldir.attributes["#{attributes_prefix}seektime"] + else + value = '' + end + } dialog = Gtk::Dialog.new(utf8(_("Change seek time")), $main_window, @@ -589,19 +595,23 @@ end def change_pano_amount(xmldir, attributes_prefix, value) $modified = true - if value.nil? - xmldir.delete_attribute("#{attributes_prefix}pano-amount") - else - xmldir.add_attribute("#{attributes_prefix}pano-amount", value.to_s) - end + rexml_thread_protect { + if value.nil? + xmldir.delete_attribute("#{attributes_prefix}pano-amount") + else + xmldir.add_attribute("#{attributes_prefix}pano-amount", value.to_s) + end + } end def ask_new_pano_amount(xmldir, attributes_prefix) - if xmldir - value = xmldir.attributes["#{attributes_prefix}pano-amount"] - else - value = nil - end + rexml_thread_protect { + if xmldir + value = xmldir.attributes["#{attributes_prefix}pano-amount"] + else + value = nil + end + } dialog = Gtk::Dialog.new(utf8(_("Specify panorama amount")), $main_window, @@ -846,16 +856,40 @@ def gen_real_thumbnail_core(type, origfile, destfile, xmldir, size, infotype) end end +$max_gen_thumbnail_threads = nil +$current_gen_thumbnail_threads = 0 +$gen_thumbnail_monitor = Monitor.new + def gen_real_thumbnail(type, origfile, destfile, xmldir, size, img, infotype) - Thread.new { + if $max_gen_thumbnail_threads.nil? + $max_gen_thumbnail_threads = 1 + $config['mproc'].to_i || 1 + end + genproc = Proc.new { push_mousecursor_wait - gen_real_thumbnail_core(type, origfile, destfile, Synchronizator.new(xmldir, $xmlaccesslock), size, infotype) + gen_real_thumbnail_core(type, origfile, destfile, xmldir, size, infotype) gtk_thread_protect { img.set(destfile) $modified_pixbufs[destfile] = { :orig => img.pixbuf, :pixbuf => img.pixbuf, :angle_to_orig => 0 } } pop_mousecursor } + usethread = false + $gen_thumbnail_monitor.synchronize { + if $current_gen_thumbnail_threads < $max_gen_thumbnail_threads + $current_gen_thumbnail_threads += 1 + usethread = true + end + } + if usethread + Thread.new { + genproc.call + $gen_thumbnail_monitor.synchronize { + $current_gen_thumbnail_threads -= 1 + } + } + else + genproc.call + end end def popup_thumbnail_menu(event, optionals, fullpath, type, xmldir, attributes_prefix, possible_actions, closures) @@ -1000,8 +1034,8 @@ def popup_thumbnail_menu(event, optionals, fullpath, type, xmldir, attributes_pr end } if !possible_actions[:can_multiple] || $selected_elements.length == 0 - menu.append(enhance = Gtk::ImageMenuItem.new(utf8(xmldir.attributes["#{attributes_prefix}enhance"] ? _("Original contrast") : - _("Enhance constrast")))) + menu.append(enhance = Gtk::ImageMenuItem.new(utf8(rexml_thread_protect { xmldir.attributes["#{attributes_prefix}enhance"] } ? _("Original contrast") : + _("Enhance constrast")))) else menu.append(enhance = Gtk::ImageMenuItem.new(utf8(_("Toggle contrast enhancement")))) end @@ -1170,13 +1204,17 @@ def add_thumbnail(autotable, filename, type, thumbnail_img, caption) cleanup_all_thumbnails.call #- refresh is not undoable and doesn't change the album, however we must regenerate all thumbnails when generating the album $modified = true - $xmldir.delete_attribute('already-generated') + rexml_thread_protect { + $xmldir.delete_attribute('already-generated') + } my_gen_real_thumbnail.call } rotate_and_cleanup = proc { |angle| cleanup_all_thumbnails.call - rotate(angle, thumbnail_img, img, $xmldir.elements["*[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y]) + rexml_thread_protect { + rotate(angle, thumbnail_img, img, $xmldir.elements["*[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y]) + } } move = proc { |direction| @@ -1207,7 +1245,9 @@ def add_thumbnail(autotable, filename, type, thumbnail_img, caption) color_swap_and_cleanup = proc { perform_color_swap_and_cleanup = proc { cleanup_all_thumbnails.call - color_swap($xmldir.elements["*[@filename='#{filename}']"], '') + rexml_thread_protect { + color_swap($xmldir.elements["*[@filename='#{filename}']"], '') + } my_gen_real_thumbnail.call } @@ -1231,7 +1271,9 @@ def add_thumbnail(autotable, filename, type, thumbnail_img, caption) change_seektime_and_cleanup_real = proc { |values| perform_change_seektime_and_cleanup = proc { |val| cleanup_all_thumbnails.call - change_seektime($xmldir.elements["*[@filename='#{filename}']"], '', val) + rexml_thread_protect { + change_seektime($xmldir.elements["*[@filename='#{filename}']"], '', val) + } my_gen_real_thumbnail.call } perform_change_seektime_and_cleanup.call(values[:new]) @@ -1252,15 +1294,19 @@ def add_thumbnail(autotable, filename, type, thumbnail_img, caption) } change_seektime_and_cleanup = proc { - if values = ask_new_seektime($xmldir.elements["*[@filename='#{filename}']"], '') - change_seektime_and_cleanup_real.call(values) - end + rexml_thread_protect { + if values = ask_new_seektime($xmldir.elements["*[@filename='#{filename}']"], '') + change_seektime_and_cleanup_real.call(values) + end + } } change_pano_amount_and_cleanup_real = proc { |values| perform_change_pano_amount_and_cleanup = proc { |val| cleanup_all_thumbnails.call - change_pano_amount($xmldir.elements["*[@filename='#{filename}']"], '', val) + rexml_thread_protect { + change_pano_amount($xmldir.elements["*[@filename='#{filename}']"], '', val) + } } perform_change_pano_amount_and_cleanup.call(values[:new]) @@ -1280,17 +1326,21 @@ def add_thumbnail(autotable, filename, type, thumbnail_img, caption) } change_pano_amount_and_cleanup = proc { - if values = ask_new_pano_amount($xmldir.elements["*[@filename='#{filename}']"], '') - change_pano_amount_and_cleanup_real.call(values) - end + rexml_thread_protect { + if values = ask_new_pano_amount($xmldir.elements["*[@filename='#{filename}']"], '') + change_pano_amount_and_cleanup_real.call(values) + end + } } whitebalance_and_cleanup_real = proc { |values| perform_change_whitebalance_and_cleanup = proc { |val| cleanup_all_thumbnails.call - change_whitebalance($xmldir.elements["*[@filename='#{filename}']"], '', val) - recalc_whitebalance(val, fullpath, thumbnail_img, img, - $xmldir.elements["*[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y], '') + rexml_thread_protect { + change_whitebalance($xmldir.elements["*[@filename='#{filename}']"], '', val) + recalc_whitebalance(val, fullpath, thumbnail_img, img, + $xmldir.elements["*[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y], '') + } } perform_change_whitebalance_and_cleanup.call(values[:new]) @@ -1310,18 +1360,22 @@ def add_thumbnail(autotable, filename, type, thumbnail_img, caption) } whitebalance_and_cleanup = proc { - if values = ask_whitebalance(fullpath, thumbnail_img, img, - $xmldir.elements["*[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y], '') - whitebalance_and_cleanup_real.call(values) - end + rexml_thread_protect { + if values = ask_whitebalance(fullpath, thumbnail_img, img, + $xmldir.elements["*[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y], '') + whitebalance_and_cleanup_real.call(values) + end + } } gammacorrect_and_cleanup_real = proc { |values| perform_change_gammacorrect_and_cleanup = Proc.new { |val| cleanup_all_thumbnails.call - change_gammacorrect($xmldir.elements["*[@filename='#{filename}']"], '', val) - recalc_gammacorrect(val, fullpath, thumbnail_img, img, - $xmldir.elements["*[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y], '') + rexml_thread_protect { + change_gammacorrect($xmldir.elements["*[@filename='#{filename}']"], '', val) + recalc_gammacorrect(val, fullpath, thumbnail_img, img, + $xmldir.elements["*[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y], '') + } } perform_change_gammacorrect_and_cleanup.call(values[:new]) @@ -1341,16 +1395,20 @@ def add_thumbnail(autotable, filename, type, thumbnail_img, caption) } gammacorrect_and_cleanup = Proc.new { - if values = ask_gammacorrect(fullpath, thumbnail_img, img, - $xmldir.elements["*[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y], '') - gammacorrect_and_cleanup_real.call(values) - end + rexml_thread_protect { + if values = ask_gammacorrect(fullpath, thumbnail_img, img, + $xmldir.elements["*[@filename='#{filename}']"], '', $default_thumbnails[:x], $default_thumbnails[:y], '') + gammacorrect_and_cleanup_real.call(values) + end + } } enhance_and_cleanup = proc { perform_enhance_and_cleanup = proc { cleanup_all_thumbnails.call - enhance($xmldir.elements["*[@filename='#{filename}']"], '') + rexml_thread_protect { + enhance($xmldir.elements["*[@filename='#{filename}']"], '') + } my_gen_real_thumbnail.call } @@ -1562,7 +1620,7 @@ def add_thumbnail(autotable, filename, type, thumbnail_img, caption) if !$ignore_next_release x, y = autotable.get_current_pos(vbox) next_ = autotable.get_next_widget(vbox) - popup_thumbnail_menu(event, ['delete'], fullpath, type, $xmldir.elements["*[@filename='#{filename}']"], '', + popup_thumbnail_menu(event, ['delete'], fullpath, type, rexml_thread_protect { $xmldir.elements["*[@filename='#{filename}']"] }, '', { :can_left => x > 0, :can_right => next_ && autotable.get_current_pos(next_)[0] > x, :can_up => y > 0, :can_down => y < autotable.get_max_y, :can_multiple => true, :can_panorama => true }, { :rotate => rotate_and_cleanup, :move => move, :color_swap => color_swap_and_cleanup, :enhance => enhance_and_cleanup, @@ -1868,7 +1926,7 @@ def ask_save_modifications(msg1, msg2, *options) #- already-generated markers in original file if $generated_outofline begin - $xmldoc = Synchronizator.new(REXML::Document.new(File.new($orig_filename)), $xmlaccesslock) + $xmldoc = REXML::Document.new(File.new($orig_filename)) mark_document_as_dirty ios = File.open($orig_filename, "w") $xmldoc.write(ios, 0) @@ -2215,10 +2273,12 @@ def sort_by_exif_date $modified = true save_changes current_order = [] - $xmldir.elements.each { |element| - if element.name == 'image' || element.name == 'video' - current_order << element.attributes['filename'] - end + rexml_thread_protect { + $xmldir.elements.each { |element| + if element.name == 'image' || element.name == 'video' + current_order << element.attributes['filename'] + end + } } #- look for EXIF dates @@ -2272,16 +2332,20 @@ def sort_by_exif_date end saves = {} - $xmldir.elements.each { |element| - if element.name == 'image' || element.name == 'video' - saves[element.attributes['filename']] = element.remove - end + rexml_thread_protect { + $xmldir.elements.each { |element| + if element.name == 'image' || element.name == 'video' + saves[element.attributes['filename']] = element.remove + end + } } neworder = smartsort(current_order, dates) - neworder.each { |f| - $xmldir.add_element(saves[f].name, saves[f].attributes) + rexml_thread_protect { + neworder.each { |f| + $xmldir.add_element(saves[f].name, saves[f].attributes) + } } #- let the auto-table reflect new ordering @@ -2335,7 +2399,7 @@ def change_dir $subalbums = Gtk::Table.new(0, 0, true) current_y_sub_albums = 0 - $xmldir = Synchronizator.new($xmldoc.elements["//dir[@path='#{$current_path}']"], $xmlaccesslock) + $xmldir = $xmldoc.elements["//dir[@path='#{$current_path}']"] $subalbums_edits = {} subalbums_counter = 0 subalbums_edits_bypos = {} @@ -2839,7 +2903,7 @@ def open_file(filename) end begin - $xmldoc = Synchronizator.new(REXML::Document.new(File.new(filename)), $xmlaccesslock) + $xmldoc = REXML::Document.new(File.new(filename)) rescue Exception $xmldoc = nil end @@ -4290,23 +4354,24 @@ def gtk_thread_protect(&proc) if Thread.current == Thread.main proc.call else - $protect_gtk_pending_calls.synchronize { + $gtk_pending_calls.synchronize { $gtk_pending_calls << proc } end end def gtk_thread_flush - #- try to lock. we cannot synchronize blindly because this might be called from - #- within the timeout flushing procs. if this is the case, not doing anything - #- should be ok since the timeout is already flushing them all. - if $protect_gtk_pending_calls.try_lock - for closure in $gtk_pending_calls + closure = nil + continue = true + begin + $gtk_pending_calls.synchronize { + closure = $gtk_pending_calls.shift + continue = $gtk_pending_calls.size > 0 + } + if closure closure.call end - $gtk_pending_calls = [] - $protect_gtk_pending_calls.unlock - end + end while continue end def ask_password_protect @@ -4560,15 +4625,10 @@ def create_main_window false } - $protect_gtk_pending_calls = Mutex.new $gtk_pending_calls = [] + $gtk_pending_calls.extend(MonitorMixin) Gtk.timeout_add(100) { - $protect_gtk_pending_calls.synchronize { - for closure in $gtk_pending_calls - closure.call - end - $gtk_pending_calls = [] - } + gtk_thread_flush true } diff --git a/lib/booh/Synchronizator.rb b/lib/booh/Synchronizator.rb deleted file mode 100644 index 35bd115..0000000 --- a/lib/booh/Synchronizator.rb +++ /dev/null @@ -1,54 +0,0 @@ -# * BOOH * -# -# A.k.a `Best web-album Of the world, Or your money back, Humerus'. -# -# The acronyn sucks, however this is a tribute to Dragon Ball by -# Akira Toriyama, where the last enemy beaten by heroes of Dragon -# Ball is named "Boo". But there was already a free software project -# called Boo, so this one will be it "Booh". Or whatever. -# -# -# Copyright (c) 2004 Guillaume Cottenceau -# -# This software may be freely redistributed under the terms of the GNU -# public license version 2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# -# -# Any method call to the wrapped object will be synchronized on a unique -# monitor. Recursive calls are possible. -# - -require 'monitor' - -class ObjectWrapper - def initialize(wrapped_object) - @__wrapped_object = wrapped_object - end - def method_missing(id, *args, &block) - __wrap(id, *args, &block) - end - def __wrap(id, *args, &block) - if block.nil? - return @__wrapped_object.__send__(id, *args) - else - return @__wrapped_object.__send__(id, *args) { |*args2| block.call(*args2) } - end - end -end - -class Synchronizator < ObjectWrapper - def initialize(wrapped_object, monitor_object) - super(wrapped_object) - @__monitor_object = monitor_object - @__monitor_object.extend(MonitorMixin) - end - def __wrap(id, *args, &block) - @__monitor_object.synchronize { - super(id, *args, &block) - } - end -end diff --git a/lib/booh/booh-lib.rb b/lib/booh/booh-lib.rb index 13e63c4..1ea0231 100644 --- a/lib/booh/booh-lib.rb +++ b/lib/booh/booh-lib.rb @@ -20,6 +20,7 @@ require 'iconv' require 'timeout' require 'tempfile' +require 'monitor' require 'booh/rexml/document' @@ -321,9 +322,11 @@ module Booh end def gen_thumbnails_element(orig, xmldirorelem, allow_background, dests) - if xmldirorelem.name == 'dir' - xmldirorelem = xmldirorelem.elements["*[@filename='#{utf8(File.basename(orig))}']"] - end + rexml_thread_protect { + if xmldirorelem.name == 'dir' + xmldirorelem = xmldirorelem.elements["*[@filename='#{utf8(File.basename(orig))}']"] + end + } gen_thumbnails(orig, allow_background, dests, xmldirorelem, '') end @@ -369,7 +372,7 @@ module Booh if entry2type(orig) == 'image' if felem - if whitebalance = felem.attributes["#{attributes_prefix}white-balance"] + if whitebalance = rexml_thread_protect { felem.attributes["#{attributes_prefix}white-balance"] } neworig = "#{dest_dir}/#{File.basename(orig)}-whitebalance#{whitebalance}.jpg" cmd = "booh-fix-whitebalance '#{orig}' '#{neworig}' #{whitebalance}" sys(cmd) @@ -377,7 +380,7 @@ module Booh orig = neworig end end - if gammacorrect = felem.attributes["#{attributes_prefix}gamma-correction"] + if gammacorrect = rexml_thread_protect { felem.attributes["#{attributes_prefix}gamma-correction"] } neworig = "#{dest_dir}/#{File.basename(orig)}-gammacorrect#{gammacorrect}.jpg" cmd = "booh-gamma-correction '#{orig}' '#{neworig}' #{gammacorrect}" sys(cmd) @@ -385,12 +388,12 @@ module Booh orig = neworig end end - rotate = felem.attributes["#{attributes_prefix}rotate"] + rotate = rexml_thread_protect { felem.attributes["#{attributes_prefix}rotate"] } if !rotate - felem.add_attribute("#{attributes_prefix}rotate", rotate = guess_rotate(orig).to_s) + rexml_thread_protect { felem.add_attribute("#{attributes_prefix}rotate", rotate = guess_rotate(orig).to_s) } end convert_options += "-rotate #{rotate} " - if felem.attributes["#{attributes_prefix}enhance"] + if rexml_thread_protect { felem.attributes["#{attributes_prefix}enhance"] } convert_options += ($config['convert-enhance'] || $convert_enhance) + " " end end @@ -421,27 +424,27 @@ module Booh elsif entry2type(orig) == 'video' if felem #- seektime is an attribute that allows to specify where the frame to use for the thumbnail must be taken - seektime = felem.attributes["#{attributes_prefix}seektime"] + seektime = rexml_thread_protect { felem.attributes["#{attributes_prefix}seektime"] } if ! seektime - felem.add_attribute("#{attributes_prefix}seektime", seektime = "0") + rexml_thread_protect { felem.add_attribute("#{attributes_prefix}seektime", seektime = "0") } end seektime = seektime.to_f - if rotate = felem.attributes["#{attributes_prefix}rotate"] + if rotate = rexml_thread_protect { felem.attributes["#{attributes_prefix}rotate"] } convert_options += "-rotate #{rotate} " end - if felem.attributes["#{attributes_prefix}enhance"] + if rexml_thread_protect { felem.attributes["#{attributes_prefix}enhance"] } convert_options += ($config['convert-enhance'] || $convert_enhance) + " " end end for dest in dests if ! File.exists?(dest['filename']) - tmpdir = gen_video_thumbnail(orig, felem && felem.attributes["#{attributes_prefix}color-swap"], seektime) + tmpdir = gen_video_thumbnail(orig, felem && rexml_thread_protect { felem.attributes["#{attributes_prefix}color-swap"] }, seektime) if tmpdir.nil? return false end tmpfile = "#{tmpdir}/00000001.jpg" alltmpfiles = [ tmpfile ] - if felem && whitebalance = felem.attributes["#{attributes_prefix}white-balance"] + if felem && whitebalance = rexml_thread_protect { felem.attributes["#{attributes_prefix}white-balance"] } if whitebalance.to_f != 0 neworig = "#{tmpdir}/whitebalance#{whitebalance}.jpg" cmd = "booh-fix-whitebalance '#{tmpfile}' '#{neworig}' #{whitebalance}" @@ -452,7 +455,7 @@ module Booh end end end - if felem && gammacorrect = felem.attributes["#{attributes_prefix}gamma-correction"] + if felem && gammacorrect = rexml_thread_protect { felem.attributes["#{attributes_prefix}gamma-correction"] } if gammacorrect.to_f != 0 neworig = "#{tmpdir}/gammacorrect#{gammacorrect}.jpg" cmd = "booh-gamma-correction '#{tmpfile}' '#{neworig}' #{gammacorrect}" @@ -529,6 +532,14 @@ module Booh ios.close end + $xmlaccesslock = Monitor.new + + def rexml_thread_protect(&proc) + $xmlaccesslock.synchronize { + proc.call + } + end + def check_browser browser_binary = $config['browser'].split.first if browser_binary && !File.executable?(browser_binary) -- 2.30.4