[yast-commit] <rest-service> backgroud_patches_bnc550934 : patches and status - moved the shared background code
ref: refs/heads/backgroud_patches_bnc550934 commit fe2894a7088810c100f1eac4a071a6911e8193ba Author: Ladislav Slezak <lslezak@novell.com> Date: Tue Dec 15 20:13:59 2009 +0100 patches and status - moved the shared background code to BackgroundManager module (for thread safe acess and better readability) --- plugins/patches/app/models/patch.rb | 53 +++++++++------------- plugins/status/lib/status.rb | 78 ++++++++++++++------------------ webservice/lib/background_manager.rb | 80 ++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 76 deletions(-) diff --git a/plugins/patches/app/models/patch.rb b/plugins/patches/app/models/patch.rb index dd861de..a3608c6 100644 --- a/plugins/patches/app/models/patch.rb +++ b/plugins/patches/app/models/patch.rb @@ -3,14 +3,10 @@ require 'resolvable' # Model for patches available via package kit class Patch < Resolvable - # class variables - they keep the information between requests - # - # currently running patch requests in background (current states) - @@running = Hash.new - # finished requests (actual results) - @@done = Hash.new - # a mutex which guards access to the shared class variables above - @@mutex = Mutex.new + # extend adds the module methods as class methods + # (used instead of include because background processes + # are used in class methods here) + extend BackgroundManager def to_xml( options = {} ) super :patch_update, options @@ -35,7 +31,7 @@ class Patch < Resolvable return patch_updates end - def self.subprocess_find(what, bs) + def self.subprocess_find(what) # open subprocess subproc = IO.popen(subprocess_command) @@ -55,7 +51,8 @@ class Patch < Resolvable result = received['patches'] elsif received.has_key? 'background_status' s = received['background_status'] - @@mutex.synchronize do + + update_progress what do |bs| bs.status = s['status'] bs.progress = s['progress'] bs.subprogress = s['subprogress'] @@ -86,42 +83,34 @@ class Patch < Resolvable # background reading doesn't work correctly if class reloading is active # (static class members are lost between requests) - if background && !Rails.configuration.cache_classes + if background && !background_enabled? Rails.logger.info "Class reloading is active, cannot use background thread (set config.cache_classes = true)" background = false end if background - # background status - bs = nil - @@mutex.synchronize do - if @@done.has_key?(what) - Rails.logger.debug "Request #{what} is done" - return @@done.delete(what) - end - - running = @@running[what] - if running - Rails.logger.debug "Request #{what} is already running: #{running.inspect}" - return [running] - end + if process_finished? what + Rails.logger.debug "Request #{what} is done" + return get_value what + end - bs = BackgroundStatus.new - @@running[what] = bs + running = get_progress what + if running + Rails.logger.debug "Request #{what} is already running: #{running.inspect}" + return [running] end + add_process what + Rails.logger.info "Starting background thread for reading patches..." # run the patch query in a separate thread Thread.new do - res = subprocess_find(what, bs) + res = subprocess_find what Rails.logger.info "*** Patches thread: Found #{res.size} applicable patches" - @@mutex.synchronize do - @@running.delete(what) - @@done[what] = res - end + finish_process(what, res) end - return [bs] + return [ get_progress(what) ] else return do_find(what) end diff --git a/plugins/status/lib/status.rb b/plugins/status/lib/status.rb index 1bcdabe..cad3412 100644 --- a/plugins/status/lib/status.rb +++ b/plugins/status/lib/status.rb @@ -10,12 +10,7 @@ class Status :metrics, :limits - # currently running status requests in background (current states) - @@running = Hash.new - # finished requests (actual results) - @@done = Hash.new - # a mutex which guards access to the shared class variables above - @@mutex = Mutex.new + include BackgroundManager def to_xml(options = {}) if @data.class == Hash && @data.size == 1 && @data[:progress].class == BackgroundStatus @@ -152,51 +147,48 @@ class Status def draw_graph(heigth=nil, width=nil) end + def create_unique_key(start, stop, data) + start.to_i.to_s + '_' + stop.to_i.to_s + '_' + data.to_s + end + # this is a wrapper to collect_data def collect_data(start=nil, stop=nil, data = %w{cpu memory disk}, background = false) # background reading doesn't work correctly if class reloading is active # (static class members are lost between requests) - if background && !Rails.configuration.cache_classes + if background && !background_enabled? Rails.logger.info "Class reloading is active, cannot use background thread (set config.cache_classes = true)" background = false end if background - # background status - bs = nil - key = start.to_i.to_s + '_' + stop.to_i.to_s + '_' + data.to_s - @@mutex.synchronize do - if @@done.has_key?(key) - Rails.logger.debug "Request #{key} is done" - @data = @@done.delete key - @data.delete :progress - return @data - end + key = create_unique_key(start, stop, data) - running = @@running[key] - if running - Rails.logger.debug "Request #{key} is already running: #{running.inspect}" - @data = {:progress => running} - return @data - end + if process_finished? key + Rails.logger.debug "Request #{key} is done" + @data = get_value key + @data.delete :progress + return @data + end - bs = BackgroundStatus.new - @@running[key] = bs + running = get_progress key + if running + Rails.logger.debug "Request #{key} is already running: #{running.inspect}" + @data = {:progress => running} + return @data end + + add_process(key) # read the status in a separate thread Thread.new do Rails.logger.info '*** Background thread for reading status started...' - res = collect_status_data(start, stop, data, bs) - @@mutex.synchronize do - @@running.delete(key) - @@done[key] = res - end + res = collect_status_data(start, stop, data, true) + finish_process(key, res) Rails.logger.info '*** Finishing background status thread' end # return current progress - @data = {:progress => bs} + @data = {:progress => get_progress(key)} return @data else collect_status_data(start, stop, data) @@ -204,8 +196,8 @@ class Status end # creates several metrics for a defined period - def collect_status_data(start=nil, stop=nil, data = %w{cpu memory disk}, progress=nil) - + def collect_status_data(start=nil, stop=nil, data = %w{cpu memory disk}, progress=false) + key = create_unique_key(start, stop, data) if progress metrics = available_metrics #puts metrics.inspect result = Hash.new @@ -213,16 +205,14 @@ class Status current_step = 0 if progress case data when nil, "all", "All" # all metrics - if progress - total_steps = metrics.size - end + total_steps = metrics.size if progress metrics.each_pair do |m, n| if progress - @@mutex.synchronize do - progress.status = m.to_s - progress.progress = 100 * current_step / total_steps - Rails.logger.debug "*** Reading status: #{progress.status}: #{progress.progress}%" + update_progress key do |stat| + stat.status = m.to_s + stat.progress = 100 * current_step / total_steps + Rails.logger.debug "*** Reading status: #{stat.status}: #{stat.progress}%" end end @@ -238,10 +228,10 @@ class Status total_steps = data.size if progress data.each do |d| if progress - @@mutex.synchronize do - progress.status = d.to_s - progress.progress = 100 * current_step / total_steps - Rails.logger.debug "*** Reading status: #{progress.status}: #{progress.progress}%" + update_progress key do |stat| + stat.status = d.to_s + stat.progress = 100 * current_step / total_steps + Rails.logger.debug "*** Reading status: #{stat.status}: #{stat.progress}%" end end diff --git a/webservice/lib/background_manager.rb b/webservice/lib/background_manager.rb new file mode 100644 index 0000000..722d078 --- /dev/null +++ b/webservice/lib/background_manager.rb @@ -0,0 +1,80 @@ + +# this is a helper module for managing background processes with progress +# for long running tasks + +module BackgroundManager + # class variables - they keep the information between requests + # DO NOT USE THEM DIRECTLY, USE THE THREAD SAFE METHODS BELOW! + # + # currently running requests in background (current states) + @@running = Hash.new + # finished requests (actual results) + @@done = Hash.new + # a mutex which guards access to the shared class variables above + @@mutex = Mutex.new + + # define a new background process + # id is unique ID + def add_process(id) + @@mutex.synchronize do + @@running[id] = BackgroundStatus.new unless @@running.has_key?(id) + end + end + + # is the process running? + def process_running?(id) + @@mutex.synchronize do + return @@running.has_key? id + end + end + + # is the process finished? + def process_finished?(id) + @@mutex.synchronize do + return @@done.has_key? id + end + end + + # remove the progress status and remember the real final value + def finish_process(id, value) + @@mutex.synchronize do + @@running.delete(id) + @@done[id] = value + end + end + + # get the current progress + # returns a copy, use update_progress() for updating the progress + def get_progress(id) + @@mutex.synchronize do + ret = @@running[id] + ret.nil? ? nil : ret.dup + end + end + + # get the final value, the value is removed from the internal structure + def get_value(id) + @@mutex.synchronize do + return @@done.delete id + end + end + + # update the progress + def update_progress(id, &block) + @@mutex.synchronize do + bs = @@running[id] + + yield bs if bs && block_given? + end + end + + # is background processing possible? + # if cache_classes is disabled it is not possible + # because all classes are reloaded between requests + # and the static attributes keeping the progress are lost + def background_enabled? + return Rails.configuration.cache_classes + end + +end + -- To unsubscribe, e-mail: yast-commit+unsubscribe@opensuse.org For additional commands, e-mail: yast-commit+help@opensuse.org
participants (1)
-
Ladislav Slezak