はじめに
この記事は Qt Advent Calendar 2021 4日目の記事です。遅れてごめんなさい。
昨日は @task_jp による Qt で Quite OK Image を表示する という記事でした。
1日目の記事 では、ソースコードレベルで Qt Project に貢献した内容を紹介しましたが、今日はバグレポートを書いて Qt Project に貢献した話を書きます。
バグレポートの作成
[QTBUG-66075] Wayland event dispatcher multi-threading support
Description
Qt apps on AGL platform freeze when they are running in background and their visibility in IVI-Shell is set to false. They can not receive any notification from D-Bus or WebSockets until visibility is set to true.
これが今日紹介したいバグレポートです。
実は Qt Advent Calendar 2018 の 「AGL の開発に今年はちょっとだけ関われました」 という記事の「Wayland 関係で Qt のイベントループが固まる件」というセクションでこのバグレポートについて以下の記載をしています。
「Wayland event dispatcher multi-threading support」 というバグレポを超頑張って書いたはいいものの、問題が私の手には負えないので、誰かなんとかして欲しいなーと思いつつ、それを実現するための良い方法が思い浮かんでいません。可能性は低いけれど、The Qt Company が AGL をやる気になった暁には最優先で直してもらおう。
その前後の流れから分かる可能性もありますが、これはバグレポートを作成した当時(2018年1月30日よりちょっと前)に AGL というプロジェクトで CES 向けのデモを作っていたりした際に、とある車メーカーの方から「AGL で Qt でアプリを書くと、アプリを非表示にしてバックグラウンドに回った際にイベントループが止まっちゃう」というフィードバックを受けて、調査したものです。
このバグはかなり難解で、
- アプリの作り方(Qt の使い方)が悪いんじゃないか
- AGL の Wayland のコンポジターの悪いんじゃないか
- R-Car の OpenGL まわりのドライバが悪いんじゃないか
という当初の想定はすべて当てはまらず、Qt の Wayland のイベントループを回しているところの実装が Wayland の推奨と違うという 神のお告げ が某所から降ってきたため、
- Wayland を使ったらこういうバグが起こったよ
- AGL でこうやったら再現できるよ(難易度高い)
- Qt の今の実装はこうなってるよ
- 本来こうあるべきらしいよ
という内容でバグレポートを作成しました。
さらに、次の日、実際に AGL のデモを開発していた P 社や R 社などのエンジニアと実際に悪さをしているのは誰かを詳細に解析し、以下のコメント を追記しました。
This is one sequence we've figured out:
- Both QPA and WSEGL(libpvrWAYLAND_WSEGL.so) are in poll()
- Wayland API is called because app changes its UI in background
- poll() in WSEGL returns back with data
- WSEGL calls reads_events()
- reader_count becomes 0, WSEGL reads data from the socket
- push events to the queue including events for others
- WSEGL skips drawing, calls poll() again
- in 6, events for Qt QPA is filled to the queue but poll() doesn't return (because flushRequests() is not written as multi-threading way)
これで、バグの原因が Qt であることはほぼ間違いないし、簡単な回避方法もないという状況になりました。
1年後
Qt の開発チームのエンジニアから「XCB で同じような問題を解決したから同じようにすればいい気がするけど、とりあえず簡単に再現できる方法ないかな?」というコメントがつきました。
1年半後
KDE の開発をしているエンジニアから「これはデスクトップでも Wayland で GStreamer を使ったら再現するぜ」というコメントがつきました。
2年半後
Ubuntu 関係の開発をしているエンジニアから「Ubuntu 20.04 で Qt の動画再生のサンプルアプリで vaapi で gstreamer で動画を再生したら普通に再現するぜ」というコメントもつきました。
3年半後
上記のエンジニアによる Move the wayland socket polling to a separate event thread という変更が1年以上のレビュー期間を経てようやく Qt にマージされ、この問題がついに解決しました。
この修正は次のマイナーアップデートの Qt 6.3 に含まれます。
おわりに
Qt を使ってアプリを作ったりシステムを作ったりして、なにかおかしなことが起こると「Qt がうまく動かない」といわれ、原因を探るとアプリのコードが悪かったり、Qt より下のレイヤーの問題だったりすることが非常に多いのですが、このケースは Qt 自体が悪かった珍しいものでした。
そして、バグ自体はかなり重要なものではありましたが、修正自体の難易度が高く(発見当時は)再現する難易度も高かったため、修正される可能性がとても低いバグでした。
だいぶ時間はかかりましたが、私が書いたバグレポートが、同じ現象で困っていた世の中の何人かの人の目にちゃんと止まり、最終的に修正までなされたということで、多くの人の役に立つことになったんじゃないかなと思います。
バグレポートって、書くの大変だし、気を遣うし、面倒くさいですが、自分が書かないと、他の人が同じ事で悩むかもしれないし、同じような調査に時間をかけるかもしれないし、バグレポートを書かないとって悩むかもしれないので、特に今回のものに関しては、苦労したけれど本当に書いてよかったなぁと思えるものでした。
明日は @hoshianaaa さんの PyQt5のチュートリアルを動かす ① Hello World です。お楽しみに!っていうか、もうちゃんと書いてくれてますね!
遅くなってごめんなさい!