From 4676aecf38532483ecc33ac5180d63b5042097f8 Mon Sep 17 00:00:00 2001 From: toshi Date: Tue, 20 Mar 2012 16:26:00 +0000 Subject: 処理を適当に切り分けて少しづつループする Enumerable#deach を追加。長時間UIスレッドを使ってしまうループでこれを使うようにした MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: svn://toshia.dip.jp/mikutter/trunk@715 03aab468-d3d2-4883-8b12-f661bbf03fa8 --- core/addon/extract.rb | 4 +-- core/addon/image_file_cache/image_file_cache.rb | 2 +- core/addon/list/liststream.rb | 2 +- core/delayer.rb | 21 ++++++++++--- core/lib/deferred/deferred.rb | 29 ++++++++---------- core/lib/deferred/deferredable.rb | 9 +++++- core/lib/deferred/test.deferred.rb | 24 +++++++-------- core/lib/mikutwitter/api_call_support.rb | 40 ++++++++++++------------- core/mui/cairo_miracle_painter.rb | 16 ++++++++-- core/mui/cairo_sub_parts_favorite.rb | 18 +++++++---- core/mui/cairo_sub_parts_retweet.rb | 8 ++--- core/mui/cairo_timeline.rb | 4 +-- core/plugin.rb | 14 +++++++-- 13 files changed, 118 insertions(+), 73 deletions(-) diff --git a/core/addon/extract.rb b/core/addon/extract.rb index 161df116..72f23633 100644 --- a/core/addon/extract.rb +++ b/core/addon/extract.rb @@ -95,13 +95,13 @@ Module.new do def hook_plugin(event) Plugin.create(:extract).add_event(event){ |service, messages| - tabclass.tabs.each{ |tab| tab.__send__("event_#{event}", messages) } } + tabclass.tabs.deach{ |tab| tab.__send__("event_#{event}", messages) } } end def boot_plugin [:update,:mention,:posted].each{ |event| hook_plugin(event) } Plugin.create(:extract).add_event(:appear){ |messages| - tabclass.tabs.each{ |tab| tab.__send__("event_appear", messages) } } + tabclass.tabs.deach{ |tab| tab.__send__("event_appear", messages) } } end def tabclass diff --git a/core/addon/image_file_cache/image_file_cache.rb b/core/addon/image_file_cache/image_file_cache.rb index 4c307fa6..c2445794 100644 --- a/core/addon/image_file_cache/image_file_cache.rb +++ b/core/addon/image_file_cache/image_file_cache.rb @@ -9,7 +9,7 @@ Plugin.create :image_file_cache do # appear_limit 回TLに出現したユーザはキャッシュに登録する # (30分ツイートしなければカウンタはリセット) onappear do |messages| - messages.each { |message| + messages.deach { |message| image_url = message.user[:profile_image_url] if not j_include?(image_url) appear_counter[image_url] ||= 0 diff --git a/core/addon/list/liststream.rb b/core/addon/list/liststream.rb index 3949213c..01e71f81 100644 --- a/core/addon/list/liststream.rb +++ b/core/addon/list/liststream.rb @@ -33,7 +33,7 @@ Plugin::create(:liststream) do member_anything - Plugin.filtering(:followings, Set.new).first end def start - service = Service.services.first + service = Service.primary Thread.new{ loop{ sleep(3) diff --git a/core/delayer.rb b/core/delayer.rb index 385664c2..b7636b1c 100644 --- a/core/delayer.rb +++ b/core/delayer.rb @@ -51,18 +51,31 @@ class Delayer debugging_wait begin @busy = true - st = Process.times.utime + @st = Process.times.utime 3.times{ |cnt| procs = [] if not @@routines[cnt].empty? then procs = @@routines[cnt].clone procs.each{ |routine| - @@routines[cnt].delete(routine) - routine.run - return if ((Process.times.utime - st) > 0.1) } end } + if Mopt.debug + r_start = Process.times.utime + @@routines[cnt].delete(routine) + routine.run + if (r_end = Process.times.utime - r_start) > 0.1 + open(File.expand_path(File.join(Environment::LOGDIR, "delayer.late.log")), "a"){ |io| + bt = routine.backtrace.find{ |bt| not bt.include?('delayer') } + bt = routine.backtrace.first if not bt + io.puts("#{r_end},#{bt.gsub(FOLLOW_DIR, '{MIKUTTER_DIR}')}") } + end + else + routine.run end + return if time_limit? } end } ensure @busy = false end end + def self.time_limit? + (Process.times.utime - @st) > 0.02 end + # Delayerのタスクを消化中ならtrueを返す def self.busy? @busy end diff --git a/core/lib/deferred/deferred.rb b/core/lib/deferred/deferred.rb index 0b2beb89..3035643b 100644 --- a/core/lib/deferred/deferred.rb +++ b/core/lib/deferred/deferred.rb @@ -5,6 +5,7 @@ class Deferred def initialize(follow = nil) @follow = follow + @backtrace = caller if Mopt.debug end alias :deferredable_cancel :cancel @@ -66,22 +67,18 @@ class Thread end module Enumerable - def aeach(&proc) - start_time = 0 - ary = to_a - limit = ary.size - index = 0 - peace = lambda{ - start_time = Time.new.to_f - while(index >= limit) - if (Time.new.to_f - start_time) >= 0.01 - peace.call - break deferred{ peace.call } - else - result = proc.call(ary[index]) - index += 1 - result end end } - deferred{ peace.call } + # 遅延each。あとで実行されるし、あんまりループに時間がかかるようなら一旦ループを終了する + def deach(&proc) + iteratee = to_a + iteratee = dup if equal?(iteratee) + deferred{ + result = nil + while not iteratee.empty? + item = iteratee.shift + proc.call(item) + if Delayer.time_limit? + break result = iteratee.deach(&proc) end end + result } end end diff --git a/core/lib/deferred/deferredable.rb b/core/lib/deferred/deferredable.rb index 98aa8705..15d07583 100644 --- a/core/lib/deferred/deferredable.rb +++ b/core/lib/deferred/deferredable.rb @@ -2,6 +2,9 @@ # なんでもDeferred module Deferredable + + attr_reader :backtrace + # このDeferredが成功した場合の処理を追加する。 # 新しいDeferredのインスタンスを返す def next(&proc) @@ -82,7 +85,11 @@ module Deferredable @next end } else if defined?(@next) - Delayer.new{ @next.call(n_value) } + if Mopt.debug + this = self + Delayer.new{ @next.call(n_value) }.instance_eval{ @backtrace = this.backtrace } + else + Delayer.new{ @next.call(n_value) } end else regist_next_call(:ok, n_value) end end throw :__deferredable_success diff --git a/core/lib/deferred/test.deferred.rb b/core/lib/deferred/test.deferred.rb index 0708b413..47c84f03 100644 --- a/core/lib/deferred/test.deferred.rb +++ b/core/lib/deferred/test.deferred.rb @@ -87,10 +87,12 @@ class TC_Deferred < Test::Unit::TestCase # !> method redefined; discarding old f Thread.new{ 39 }.next{ |x| - ans = x # !> method redefined; discarding old _post + x + 1 # !> method redefined; discarding old _post + }.next{ |x| + ans = x } wait_all_tasks - assert_equal(39, ans) + assert_equal(40, ans) end def test_thread_error @@ -184,19 +186,17 @@ class TC_Deferred < Test::Unit::TestCase # !> method redefined; discarding old f assert_equal(0, ans) end - # def test_aeach - # a = 0 - # (1..1000000).aeach{ - # Time.new - # a += 1 - # } - # Delayer.run - # assert_equal(1000000, a) - # end + def test_deach + a = 0 + (1..100000).deach{ + a += 1 + } + wait_all_tasks + assert_equal(100000, a) + end end - # >> Loaded suite - # >> Started # >> F.F.... diff --git a/core/lib/mikutwitter/api_call_support.rb b/core/lib/mikutwitter/api_call_support.rb index 7d172944..bdf0c99b 100644 --- a/core/lib/mikutwitter/api_call_support.rb +++ b/core/lib/mikutwitter/api_call_support.rb @@ -41,7 +41,7 @@ module MikuTwitter::ApiCallSupport define_method(multi){ |options = {}| type_strict options => Hash json(defaults.merge(options)).next{ |node| - node.map(&parser) } } + Thread.new{ node.map(&parser) } } } define_method(uni){ |options = {}| type_strict options => Hash @@ -69,7 +69,7 @@ module MikuTwitter::ApiCallSupport def json(options) type_strict options => Hash twitter.api(api, options, force_oauth).next{ |res| - JSON.parse(res.body).symbolize } end + Thread.new{ JSON.parse(res.body).symbolize } } end defparser :user defparser :message, :messages, include_entities: 1 @@ -79,7 +79,7 @@ module MikuTwitter::ApiCallSupport def messages(options = {}) type_strict options => Hash - json({include_entities: 1}.merge(options)).next(&Parser.method(:messages)) end + json({include_entities: 1}.merge(options)).next{ |m| Thread.new{ Parser.messages m } } end def friendship(options = {}) type_strict options => Hash @@ -93,23 +93,23 @@ module MikuTwitter::ApiCallSupport def search(options = {}) type_strict options => Hash json(options).next{ |res| - res[:results].map{ |msg| - cnv = msg.convert_key(:text => :message, - :to_user_id => :receiver, - :in_reply_to_status_id => :replyto) - user = { - id: msg[:from_user_id], - idname: msg[:from_user], - name: msg[:from_user_name], - profile_image_url: msg[:profile_image_url] - } - cnv[:user] = Message::MessageUser.new(User.new_ifnecessary(user), user) - if cnv[:source].is_a?(String) and - cnv[:source].gsub(/&\w+?;/){ |m| HTML_ATTR_UNESCAPE_HASH[m] }.match(/^(.*?)<\/a>$/) - cnv[:source] = $1 end - cnv[:created] = (Time.parse(msg[:created_at]) rescue Time.now) - Message.new_ifnecessary(cnv) - } } end + Thread.new { + res[:results].map{ |msg| + cnv = msg.convert_key(:text => :message, + :to_user_id => :receiver, + :in_reply_to_status_id => :replyto) + user = { + id: msg[:from_user_id], + idname: msg[:from_user], + name: msg[:from_user_name], + profile_image_url: msg[:profile_image_url] + } + cnv[:user] = Message::MessageUser.new(User.new_ifnecessary(user), user) + if cnv[:source].is_a?(String) and + cnv[:source].gsub(/&\w+?;/){ |m| HTML_ATTR_UNESCAPE_HASH[m] }.match(/^(.*?)<\/a>$/) + cnv[:source] = $1 end + cnv[:created] = (Time.parse(msg[:created_at]) rescue Time.now) + Message.new_ifnecessary(cnv) } } } end def inspect "#<#{MikuTwitter::ApiCallSupport::Request}: #{@api}>" diff --git a/core/mui/cairo_miracle_painter.rb b/core/mui/cairo_miracle_painter.rb index 8f493b89..991c9e85 100644 --- a/core/mui/cairo_miracle_painter.rb +++ b/core/mui/cairo_miracle_painter.rb @@ -38,7 +38,8 @@ class Gdk::MiraclePainter < Gtk::Object # @@miracle_painters = Hash.new - # _message_ を内部に持っているGdk::MiraclePainterの集合をSetで返す + # _message_ を内部に持っているGdk::MiraclePainterの集合をSetで返す。 + # ログ数によってはかなり重い処理なので注意 def self.findbymessage(message) type_strict message => :to_message message = message.to_message @@ -47,7 +48,18 @@ class Gdk::MiraclePainter < Gtk::Object found = tl.get_record_by_message(message) result << found.miracle_painter if found } result.freeze - # @@miracle_painters[message.to_message[:id].to_i] || EMPTY + end + + # findbymessage のdeferred版。 + def self.findbymessage_d(message) + type_strict message => :to_message + message = message.to_message + result = Set.new + Gtk::TimeLine.timelines.deach{ |tl| + found = tl.get_record_by_message(message) + result << found.miracle_painter if found + }.next{ + result.freeze } end def initialize(message, *coodinate) diff --git a/core/mui/cairo_sub_parts_favorite.rb b/core/mui/cairo_sub_parts_favorite.rb index edd51d9a..02dde4f9 100644 --- a/core/mui/cairo_sub_parts_favorite.rb +++ b/core/mui/cairo_sub_parts_favorite.rb @@ -21,18 +21,24 @@ class Gdk::SubPartsFavorite < Gdk::SubPartsVoter Delayer.new{ Plugin.create(:core) do onfavorite do |service, user, message| - Gdk::MiraclePainter.findbymessage(message).each{ |mp| - mp.subparts.find{ |sp| sp.class == Gdk::SubPartsFavorite }.add(user) } + Gdk::MiraclePainter.findbymessage_d(message).next{ |mps| + mps.deach{ |mp| + if not mp.destroyed? + mp.subparts.find{ |sp| sp.class == Gdk::SubPartsFavorite }.add(user) end } } end on_before_favorite do |service, user, message| - Gdk::MiraclePainter.findbymessage(message).each{ |mp| - mp.subparts.find{ |sp| sp.class == Gdk::SubPartsFavorite }.add(user) } + Gdk::MiraclePainter.findbymessage_d(message).next{ |mps| + mps.deach{ |mp| + if not mp.destroyed? + mp.subparts.find{ |sp| sp.class == Gdk::SubPartsFavorite }.add(user) end } } end on_fail_favorite do |service, user, message| - Gdk::MiraclePainter.findbymessage(message).each{ |mp| - mp.subparts.find{ |sp| sp.class == Gdk::SubPartsFavorite }.delete(user) } + Gdk::MiraclePainter.findbymessage_d(message).next{ |mps| + mps.deach{ |mp| + if not mp.destroyed? + mp.subparts.find{ |sp| sp.class == Gdk::SubPartsFavorite }.delete(user) end } } end end } diff --git a/core/mui/cairo_sub_parts_retweet.rb b/core/mui/cairo_sub_parts_retweet.rb index c3aa36e7..2878397d 100644 --- a/core/mui/cairo_sub_parts_retweet.rb +++ b/core/mui/cairo_sub_parts_retweet.rb @@ -20,14 +20,14 @@ class Gdk::SubPartsRetweet < Gdk::SubPartsVoter Delayer.new{ Plugin.create(:core).add_event(:retweet){ |retweets| - retweets.each{ |retweet| - Delayer.new{ - Gdk::MiraclePainter.findbymessage(retweet.retweet_source(true)).each{ |mp| + retweets.deach{ |retweet| + Gdk::MiraclePainter.findbymessage_d(retweet.retweet_source(true)).next{ |mps| + mps.deach{ |mp| if not mp.destroyed? begin mp.subparts.find{ |sp| sp.class == Gdk::SubPartsRetweet }.add(retweet[:user]) mp.on_modify rescue Gtk::MiraclePainter::DestroyedError - nil end end } } } } } + nil end end } }.terminate("retweet error") } } } end diff --git a/core/mui/cairo_timeline.rb b/core/mui/cairo_timeline.rb index 4ed72332..6c1a652f 100644 --- a/core/mui/cairo_timeline.rb +++ b/core/mui/cairo_timeline.rb @@ -140,7 +140,7 @@ class Gtk::TimeLine remove_if_exists_all(removes) retweets, appends = *messages.partition{ |m| m[:retweet] } add_retweets(retweets) - appends.sort_by{ |m| -(m.modified.to_i) }.each(&method(:block_add)) + appends.sort_by{ |m| -(m.modified.to_i) }.deach(&method(:block_add)) end # リツイートを追加する。 _messages_ には Message の配列を指定し、それらはretweetでなければならない @@ -199,7 +199,7 @@ class Gtk::TimeLine when message[:rule] == :destroy remove_if_exists_all([message]) when message.retweet? - add_retweets([messages]) + add_retweets([message]) else _add(message) end end end self end diff --git a/core/plugin.rb b/core/plugin.rb index b595bd95..c5fb2dc0 100644 --- a/core/plugin.rb +++ b/core/plugin.rb @@ -258,7 +258,16 @@ class Plugin # plugin_loopの簡略化版。プラグインに引数 _args_ をそのまま渡して呼び出す def plugin_callback_loop(ary, event_name, kind, *args) plugin_loop(ary, event_name, kind){ |tag, proc| - proc.call(*args){ throw(:plugin_exit) } } end + if Mopt.debug + r_start = Process.times.utime + result = proc.call(*args){ throw(:plugin_exit) } + if (r_end = Process.times.utime - r_start) > 0.1 + open(File.expand_path(File.join(Environment::LOGDIR, "plugin.late.log")), "a"){ |io| + notice "#{r_end},#{tag.name},#{event_name},#{kind}" + io.puts("#{r_end},#{tag.name},#{event_name},#{kind}") } end + result + else + proc.call(*args){ throw(:plugin_exit) } end } end # _ary_ [ _event\_name_ ] に登録されているプラグイン一つひとつを引数に _proc_ を繰り返し呼ぶ。 # _proc_ のシグニチャは以下の通り。 @@ -285,7 +294,8 @@ class Plugin # ブロックの実行時間を記録しながら実行 def call_routine(plugintag, event_name, kind) - catch(:plugin_exit){ yield } end + catch(:plugin_exit){ yield } + end # 登録済みプラグインの一覧を返す。 # 返すHashは以下のような構造。 -- cgit v1.2.3