致命的 #995
完了CPU使用率が常に100%になり、メモリを大量に確保しつづけることがある
toshi_a 初音 さんが7年以上前に追加. 6年以上前に更新.
0%
説明
CPU使用率が100%となり、使用メモリは毎秒+100MBくらいのペースで増加する。この間、多少動作が重いがmikutterのUIを操作することは出来る。
発生条件は不明で、発生するときは5分くらい使っていたら発生するし、しないときは数日再現しないことがある。
Delayer Deferred 1.1.0にしてから発生するようになったと思われる。プラグインでSample Streamを受信するなどしても発生頻度は変わっていないようなので、ある種類のツイートがトリガーになっているというわけでもないかもしれない。
私以外誰も再現していないようなので優先度は下げるが、この状態になれば数分以内にメモリを食い尽くしてクラッシュするので、致命的な問題として取り扱う。
他に再現した人がいたら、新たなことが分からなくても良いので、このチケットにコメントを残してください。
ファイル
スクリーンショット 2017-05-21 18.30.58.png (406 KB) スクリーンショット 2017-05-21 18.30.58.png | あひる 家鴨, 2017-05-21 18:41 | ||
スクリーンショット 2017-05-21 18.31.15.png (374 KB) スクリーンショット 2017-05-21 18.31.15.png | あひる 家鴨, 2017-05-21 18:41 |
バックトレース
なし
再現手順
不明
関連するチケット
toshi_a 初音 さんが7年以上前に更新
- ステータス を 新規 から 実装待ち に変更
どうやらdelayer-deferredの不正な使い方が問題なのではないかと考えた。
現在のdelayer-deferredはJavaScriptのものをほとんど直訳しただけで、nextメソッドを同じDeferredに対して複数回呼ぶなどの不正な操作を受け付けてしまい、その際の動作が未定義だった。
大幅に書き直したdeferred 2.0を試作して、mikutterにそちらを利用させることで詳細なエラーを得て、3.5にもフィードバックしたい。
toshi_a 初音 さんが7年以上前に更新
deferred 2.0を利用したところ、起動直後に以下のようなエラーでクラッシュする。
/home/toshi/Projects/delayer-deferred/lib/delayer/deferred/deferredable/node_sequence.rb:29:in `pull': Invalid sequence flow `stop' to `await'. from /home/toshi/Projects/delayer-deferred/lib/delayer/deferred/deferredable/node_sequence.rb:122:in `change_sequence' from /home/toshi/Projects/delayer-deferred/lib/delayer/deferred/deferredable/awaitable.rb:20:in `enter_await' from /home/toshi/Projects/delayer-deferred/lib/delayer/deferred/request.rb:38:in `accept_request' from /home/toshi/Projects/delayer-deferred/lib/delayer/deferred/worker.rb:34:in `block in push' from /home/toshi/Projects/mikutter/vendor/bundle/ruby/2.4.0/gems/delayer-0.0.2/lib/delayer/procedure.rb:24:in `run' from /home/toshi/Projects/mikutter/vendor/bundle/ruby/2.4.0/gems/delayer-0.0.2/lib/delayer/extend.rb:58:in `run_once' from /home/toshi/Projects/mikutter/vendor/bundle/ruby/2.4.0/gems/delayer-0.0.2/lib/delayer/extend.rb:30:in `run' from /home/toshi/Projects/mikutter/vendor/bundle/ruby/2.4.0/gems/delayer-0.0.2/lib/delayer.rb:43:in `method_missing' from /home/toshi/Projects/mikutter/core/plugin/gtk/delayer.rb:10:in `block in boot' from /home/toshi/Projects/mikutter/core/plugin/gtk/mainloop.rb:10:in `main' from /home/toshi/Projects/mikutter/core/plugin/gtk/mainloop.rb:10:in `mainloop' from /home/toshi/Projects/mikutter/mikutter.rb:67:in `boot!' from /home/toshi/Projects/mikutter/mikutter.rb:96:in `<main>'
2.0ではDeferredチェインベースのバックトレースも生成できるので、こういう時にそれが出力できたほうが良さそう
Akira Ouchi さんが7年以上前に更新
- Mac OS X 10.10.5
- ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin14]
- mikutter hotfix/3.5 branch commit ce152e52a44c691ed55317a44192513fa94c5b54
Gemfileで1.0.4にバージョン固定にしてやると回避できました。(再現を試すときは1.1.1で固定)
# gem 'delayer-deferred', '>= 1.0.4', '< 2.0' # gem 'delayer-deferred', '1.1.1' gem 'delayer-deferred', '1.0.4'
参考になるのかわからないですが、起動後にCtrl+Cで終了させた時の出力を貼っておきます。
notice: {MIKUTTER_DIR}/core/configloader.rb:32:in `block in <module:ConfigLoader>': configloader: wrote 4 keys (Plugin::saved_search_last_saved_search_state, UserConfig::extract_tabs, UserConfig::ui_tab_order, UserConfig::profile_opened_tabs) error: /Users/akkie/.mikutter/plugin/slack/api/realtime.rb:50:in `block in connected': 784: unexpected token at 'You are sending too many requests. Please relax.' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/json_pure-1.8.6/lib/json/common.rb:155:in `parse' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/json_pure-1.8.6/lib/json/common.rb:155:in `parse' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/faraday_middleware-0.10.1/lib/faraday_middleware/response/parse_json.rb:11:in `block in <class:ParseJson>' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/faraday_middleware-0.10.1/lib/faraday_middleware/response_middleware.rb:48:in `parse' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/faraday_middleware-0.10.1/lib/faraday_middleware/response_middleware.rb:39:in `process_response' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/faraday_middleware-0.10.1/lib/faraday_middleware/response_middleware.rb:32:in `block in call' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/faraday-0.12.1/lib/faraday/response.rb:61:in `on_complete' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/faraday_middleware-0.10.1/lib/faraday_middleware/response_middleware.rb:30:in `call' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/faraday-0.12.1/lib/faraday/request/url_encoded.rb:15:in `call' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/faraday-0.12.1/lib/faraday/request/multipart.rb:15:in `call' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/faraday-0.12.1/lib/faraday/rack_builder.rb:139:in `build_response' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/faraday-0.12.1/lib/faraday/connection.rb:386:in `run_request' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/faraday-0.12.1/lib/faraday/connection.rb:186:in `post' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/slack-api-1.3.0/lib/slack/request.rb:29:in `request' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/slack-api-1.3.0/lib/slack/request.rb:11:in `post' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/slack-api-1.3.0/lib/slack/endpoint/users.rb:67:in `users_list' from /Users/akkie/.mikutter/plugin/slack/api.rb:32:in `block in users' warning: {MIKUTTER_DIR}/core/lib/reserver.rb:72:in `rescue in block (2 levels) in waiter': undefined method `each' for nil:NilClass from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/2.3.0/set.rb:674:in `each' from {MIKUTTER_DIR}/core/lib/reserver.rb:62:in `first' from {MIKUTTER_DIR}/core/lib/reserver.rb:62:in `block (2 levels) in waiter' warning: {MIKUTTER_DIR}/core/lib/reserver.rb:72:in `rescue in block (2 levels) in waiter': undefined method `each' for nil:NilClass from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/2.3.0/set.rb:674:in `each' from {MIKUTTER_DIR}/core/lib/reserver.rb:62:in `first' from {MIKUTTER_DIR}/core/lib/reserver.rb:62:in `block (2 levels) in waiter' notice: {MIKUTTER_DIR}/core/plugin/streaming/filter.rb:62:in `block (2 levels) in start': retry wait 0, fail_count 0 ^C/Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/extend.rb:127:in `lock': Interrupt from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/priority.rb:34:in `register' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/procedure.rb:11:in `initialize' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer.rb:49:in `new' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer.rb:49:in `initialize' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:68:in `new' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:68:in `_call' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:29:in `fail' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:156:in `_fail_action' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:120:in `_fiber_completed' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:87:in `block in _call' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/procedure.rb:24:in `run' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/extend.rb:58:in `run_once' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/extend.rb:30:in `run' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer.rb:43:in `method_missing' from /Users/akkie/mikutter/core/plugin/gtk/delayer.rb:10:in `block in boot' from /Users/akkie/mikutter/core/plugin/gtk/mainloop.rb:10:in `main' from /Users/akkie/mikutter/core/plugin/gtk/mainloop.rb:10:in `mainloop' from mikutter.rb:67:in `boot!' from mikutter.rb:96:in `<main>' notice: mikutter.rb:73:in `rescue in boot!': catch exception `SystemExit' notice: mikutter.rb:75:in `rescue in boot!': => `SystemExit' notice: mikutter.rb:100:in `rescue in <main>': catch SystemExit notice: mikutter.rb:84:in `error_handling!': catch Interrupt /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/priority.rb:34:in `register': Interrupt from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/procedure.rb:11:in `initialize' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer.rb:49:in `new' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer.rb:49:in `initialize' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:68:in `new' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:68:in `_call' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:29:in `fail' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:156:in `_fail_action' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:120:in `_fiber_completed' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-1.1.1/lib/delayer/deferred/deferredable.rb:87:in `block in _call' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/procedure.rb:24:in `run' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/extend.rb:58:in `run_once' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/extend.rb:30:in `run' from /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer.rb:43:in `method_missing' from /Users/akkie/mikutter/core/plugin/gtk/delayer.rb:10:in `block in boot' from /Users/akkie/mikutter/core/plugin/gtk/mainloop.rb:10:in `main' from /Users/akkie/mikutter/core/plugin/gtk/mainloop.rb:10:in `mainloop' from mikutter.rb:67:in `boot!' from mikutter.rb:96:in `<main>'
toshi_a 初音 さんが7年以上前に更新
- 優先度 を 低め から 通常 に変更
他の環境で再現できたということなので本格的に対応を始めます。
やはりDeferred 1.1とmikutter 3.5以降の組み合わせで確定かと思います。現在Deferred 2を使って修正中なので、修正が完了したら再現テストをお願いすると思います。
他にも再現した人が居たら、引き続き情報お願いします
あひる 家鴨 さんが7年以上前に更新
- ファイル スクリーンショット 2017-05-21 18.30.58.png スクリーンショット 2017-05-21 18.30.58.png を追加
- ファイル スクリーンショット 2017-05-21 18.31.15.png スクリーンショット 2017-05-21 18.31.15.png を追加
再現しました
macOS 10.12.4
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin16]
develop branch 33e70f7e06914e046a1d2a055f6d157d37f3167a
delayer-deferred (1.1.1)
ruby については 85 ~ 97 % あたりを常にうろうろしていて、メモリの使用量はどんどん増えていく感じです
メモリに関しては書いている時点で 14 GB /16 GB ほど持って行かれているので16になるまで観察します。
toshi_a 初音 さんが7年以上前に更新
@ahiru まあ観察はいいですよ。このあとチケットに追記しますが、このバグ修正はほぼ終わっているので、それを公開したら、その修正のテストを手伝ってください。今のところは、再現手順を確立してもらえばそれで十分なので、delayer 1.0系に戻して普段は過ごしてください。
このバグは1/3ゆゆ式程度(7分強)の時間で発生することが多いので、念の為ゆゆ式を視聴しながら確実に再現できるかどうか確認しておいてもらえるとありがたいです。
toshi_a 初音 さんが7年以上前に更新
Delayer Deferred 2.0を使えば手元の環境でも問題が起こらないことを確認しました。
ひとまず、 topic/995-deferred-2
というbranchで問題が再現しないか確認してもらえますか。
ただ、ライブラリのメジャーバージョンを上げるのはmikutter 3.6からにしようと思っているので、3.5のためにDelayer Deferred 1.1でも原因を特定して修正する必要があります。
Akira Ouchi さんが7年以上前に更新
topic/995-deferred-2ブランチでは、fileselectとかdirselectみたいなのを呼び出すと以下の内容でクラッシュするようです(覚書)。
FiberError fiber called across stack rewinding barrier /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-deferred-2.0.0/lib/delayer/deferred/worker.rb:35:in `block in push' /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/procedure.rb:24:in `run' /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/extend.rb:58:in `run_once' /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/extend.rb:30:in `run' /Users/akkie/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer.rb:43:in `method_missing' {MIKUTTER_DIR}/core/plugin/gtk/delayer.rb:10:in `block in boot' {MIKUTTER_DIR}/core/mui/gtk_form_dsl.rb:313:in `run' {MIKUTTER_DIR}/core/mui/gtk_form_dsl.rb:313:in `block in fsselect' {MIKUTTER_DIR}/core/plugin/gtk/mainloop.rb:10:in `main' {MIKUTTER_DIR}/core/plugin/gtk/mainloop.rb:10:in `mainloop' {MIKUTTER_DIR}/mikutter.rb:68:in `boot!' {MIKUTTER_DIR}/mikutter.rb:104:in `<main>'
Izumi Tsutsui さんが7年以上前に更新
toshi_a 初音 さんは書きました:
ただ、ライブラリのメジャーバージョンを上げるのはmikutter 3.6からにしようと思っているので、3.5のためにDelayer Deferred 1.1でも原因を特定して修正する必要があります。
作者の意向をガン無視して、せっかちに NetBSD/i386 7.1 + pkgsrc + mikutter 3.5.7 で
#916 の bee6c2f5 c2ce3ceb を当てたうえで topic/995-deferred-2 の 4fb3ba5b を当てて
delayer-deferred 2.0.0 を入れると、 mikutter 3.5.7 でも本体は普通に起動してフォロー・フォロワーも
正しく取れているようです。
しばらくこれで動かして様子を見てみます。
toshi_a 初音 さんが7年以上前に更新
どうやらDeferred 2を使えば発生しない問題のようですね。
#995-10 のような問題はあるにせよ、これの解決は簡単そうなので、3.5でもDeferred 2に移行してしまったほうが良いかもしれません。内部は大幅に書き換わってしまってますが、Deferredにモンキーパッチしているやつはとくに見当たらないので。
topic/995-deferred-2
ブランチで特定のプラグインが入ってると動作が変わる、クラッシュするといったことがあったら教えてください。
Izumi Tsutsui さんが7年以上前に更新
履歴10 #995-10 とほぼ同じですが mikutter-uwm-hommage で画像ファイルを選択した瞬間くらいで
以下のトレースを吐いて落ちました(bugreport済み)
FiberError fiber called across stack rewinding barrier /usr/pkg/lib/ruby/gems/2.3.0/gems/delayer-deferred-2.0.0/lib/delayer/deferred/worker.rb:53:in `block in give_response' /usr/pkg/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/procedure.rb:24:in `run' /usr/pkg/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/extend.rb:58:in `run_once' /usr/pkg/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer/extend.rb:30:in `run' /usr/pkg/lib/ruby/gems/2.3.0/gems/delayer-0.0.2/lib/delayer.rb:43:in `method_missing' {MIKUTTER_DIR}/core/plugin/gtk/delayer.rb:10:in `block in boot' {MIKUTTER_DIR}/core/plugin/gtk/mainloop.rb:10:in `main' {MIKUTTER_DIR}/core/plugin/gtk/mainloop.rb:10:in `mainloop' {MIKUTTER_DIR}/mikutter.rb:65:in `boot!' {MIKUTTER_DIR}/mikutter.rb:101:in `<main>'
toshi_a 初音 さんが7年以上前に更新
- 関連している バグ #994: delayer-deferrd 1.1.0を使うと特定の操作でmikutterが落ちる を追加
toshi_a 初音 さんが7年以上前に更新
随分問題が整理できてきたので一度まとめます。
Deferred 2.0の仕様変更の弊害について¶
Deferred 1.1は、FiberErrorが発生したらFiberを現在のThreadで作り直し、そちらで処理を続行するようになっていました。また、Fiber作成のオーバヘッドを節約するため、FiberにDeferredチェインを送り込んで処理結果を親が受け取るような仕組みでした(恐らくこのあたりの実装の深淵に深淵が深淵しているんじゃないかなぁと思っていますが、見るべきものが多すぎてデバッガを使っても調査は困難でした)。
Deferred 2.0では、意図しないタイミングで特定の操作をされた時にそれを検出してクラッシュさせる「Sequence」という仕組みを導入し、Fiberの最適化をやめて安全側に倒しています。管理すべき一連の状態がSequenceを基準とするようになったため不正な状態遷移を事前に検出してより正確なエラーを報告できるようになりました。
しかしこれが原因で、Fiber側から Fiber.yield
が呼ばれた位置と現在Sequenceの対応が取れている必要が出てきたため、FiberErrorが発生したらとりあえず作り直すのでは正しく動作しなくなりました(#995-13)。
Delayerと、Gtkの再帰メインループの相性の悪さ¶
理想をいえば、mikutterでGtkの再帰メインループの使用を禁止したいところです。再帰メインループの本来の用途をすべて理解しているわけではないですが、現在のmikutterやプラグインは、ダイアログボックスなどの手続きを同期的に記述するために使っています。これは現代ではDeferredを使えば解決できる問題であり、ファイル選択やカラーピッカーなどのカスタムダイアログを除けばmikutter 3.6では専用のDSLメソッドが存在します(#1034)。
当然これを禁止すれば互換性が無くなるため、 #1034 Dialog DSLのようにGtkの再帰メインループより楽な機能を提供して、再帰メインループを使うモチベーションを開発者から削いで自然消滅を狙おうと思います。
Delayerの再帰モード¶
根本的にはこれはDeferredの問題というよりは、Delayerが再帰的に呼ばれることを想定していないことが原因です。
うまくいくかはやってみないとわかりませんが、
- 再帰メインループに入ったら現在のDelayerキューを退避し、新たなDelayerキューと差し替える
- 再帰メインループ中にプラグインがDelayerジョブを追加すると、新たなDelayerキューに追加される
- 再帰メインループ中は、新しいほうのDelayerキューのジョブのみを消化する
- 再帰メインループを抜ける寸前に、新しいほうのDelayerジョブをすべて消化し、1で退避したDelayerキューに戻す
みたいな実装をすれば、Deferredが異なる再帰メインループレベルで実行されることは防げます。
Delayerを差し替えると書いてますが、現実的にはPluggaloidのコンストラクタに利用するDelayerを渡し、これを変更することはできないのでDelayer側に退避する機能をもたせるか、そういう機能を持ったDelayerのwrapperを作るのが良さそうです。
こうしてしまうと、退避されているジョブの実行が大幅に遅れるため、ほかの不具合を誘発する可能性があります。とりあえずDelayerかmikutter本体を変更して様子を見つつ、vanillaの状態では再帰メインループの利用をやめるという方針でいきます。
toshi_a 初音 さんが7年以上前に更新
Fiber側から Fiber.yield が呼ばれた位置と現在Sequenceの対応が取れている必要が出てきたため、FiberErrorが発生したらとりあえず作り直すのでは正しく動作しなくなりました
#995-15 ではawaitを失念していて、結局awaitはFiber.yieldが必要になります。
Delayerの再帰モードについては、1.の
再帰メインループに入ったら
の部分の検出に失敗しました。
一応Gtkの機能のひとつに、再帰レベルが上がった時にコールバックされそうなメソッドを見つけましたが、Dialog#runでは呼び出されず、再帰レベルを数値で返すメソッドも、Dialog#runのあとでも同じレベルになっているようでした。
Delayer.runの再帰レベルが上がったことを判定に使おうともしましたが、mikutterのバンドルプラグインは偶然にもDelayerライフサイクルの間に再帰メインループに入ることが無く、全てGtkのイベントハンドラのコールバック内でDialog#runを呼び出していました(ボタンを押したらダイアログを表示、みたいな使い方をしているので当然といえば当然)。
- async/awaitみたいなのをやろうとしたら現状のコードをうまく動かすしかない
- 再帰メインループの仕組みがよくわからず扱いかねている
以上のことから
- 短期的には標準プラグインで再帰メインループ使用をすべて廃して、サードパーティプラグインを入れなければとりあえず問題を発生しないようにし、
- 長期的には、RubyのFiberの実装とGtk2の再帰メインループが何なのかを理解して、適切な実装を目指す
という方向性で行きます。標準プラグインに限って言えば、短期的な対策が終われば、Deferred 2.0にアップグレードすることが出来るはずで、そうなれば #916 や #994 が一歩前進します(ただし、一部のサードパーティプラグインが正しく動かなくなることがわかっているのでリリースはできない)。
toshi_a 初音 さんが約7年前に更新
再帰メインループについてはRubyでいじっている範囲でどういった振る舞いをするか確認したので別の場所に書いた。
http://toshi-a.hatenablog.com/entry/2017/08/30/070435