dyubicuoa / OpenRTM_aist_paho_mqtt_interface

MIT License
2 stars 1 forks source link

OutPortPahoPubJsonで開始したスレッドの終了処理について #4

Closed Nobu19800 closed 3 years ago

Nobu19800 commented 3 years ago

OutPortPahoPubJson.pyの以下の部分でスレッドを開始していますが、ソースコードを見た感じSIGINTシグナル以外でcatch_signal関数が終了しないので、OutPortPahoPubJsonクラスのコンストラクタが呼ばれるたびにスレッドが増殖するという事にはならないのでしょうか?

  def __init__(self):
    global called
    PahoPublisher.__init__(self)
    self._rtcout = OpenRTM_aist.Manager.instance().getLogbuf("OutPortPahoPubJson")
    self._properties = None
    thread = threading.Thread(target=self.catch_signal)
    thread.daemon = True
    thread.start()
    called = True
dyubicuoa commented 3 years ago

現状ではご指摘の通りかと思います。実は開発当初、スレッドによるシャットダウンフックを導入する予定はありませんでした。RTCの正常終了時にBrokerからのdisconnectを実行するデストラクタを呼びたい、という要望があり、なんとかして確実にデストラクタを呼べないかと考えたうえでの苦肉の策となります。Pythonにおいて確実にデストラクタが実行される保証がないのはご存知のところだと思います。

そこでご相談なのですが、スレッドを使わずに、例えばRTSystemEditorにてデータポートを操作するところの”切断”にて各種クリーンアップを行う手段はありませんでしょうか。通信モジュールにおけるコード中の"unsubscribeInterface()"がこれに該当するのかと考えましたが、ミドルウェア側からは呼ばれていないように見えます。

もし可能であればアドバイスいただければ幸いです。よろしくお願いいたします。

Nobu19800 commented 3 years ago

申し訳ありませんが、unsubscribeInterface()が呼ばれないのはバグです。去年masterブランチのソースコードではunsubscribeInterface()が呼ばれるように修正したのですが、諸々の事情によりOpenRTM-aist 1.2.2では修正は反映させてません。 1.1、1.2の場合はInPortConsumerでコネクタ切断時に呼ばれる関数がないため、コネクタの切断ごとにクリーンアップをするにはmasterブランチのソースコードを使ってもらうしかないです。 ただし、RTC終了時に処理できればいいのであれば、マネージャー終了時に関数を呼ぶ機能があるためそれを使うことはできます。 以下はManagerActionListenerの使用例です。

ManagerActionListenerクラスを定義してaddManagerActionListener関数で設定できます。 マネージャはInPortCunsumerの中でもOpenRTM_aist.Manager.instance()で取得できるので登録できると思います。

dyubicuoa commented 3 years ago

情報ありがとうございました。そうですか、ver. 1.1、1.2ではunsubscribeInterface()が利用できないのですね。。 代替手段としてご提示いただいたManagerActionListenerクラスを利用する方向で本件の修正を試みてみます。 もし不明な点があれば質問させていただくことがあるかもしれません。よろしくお願いいたします。 修正が完了しましたらご報告させていただきます。

dyubicuoa commented 3 years ago

修正完了いたしました。 これでRTCのdeactivate後に、Ctrl+Cによる終了、RTSystemEditorにおけるRTCのExit、もしくはRTShellにおけるrtexit実行、のいずれかのタイミングで通信モジュールのクリーンアップを行えるようになりました。 ご確認いただければ幸いです。 よろしくお願いいたします。

Nobu19800 commented 3 years ago

使用例ではManagerActionListenerを継承していたはずですが、OutPortPahoPubJson.pyではManagerActionListenersを継承しています。同じ名前のメンバ関数を定義しているので動作は変わらないとは思いますが。 あと、postShutdown関数でOutPortPahoPubJsonの__del__関数を呼んでいますが、デストラクタは実行する保証がないだけなので、デストラクタがどこかで実行された場合に2回__del__関数を実行してしまうという事にはならないのでしょうか?

dyubicuoa commented 3 years ago

お忙しいところ、ソースコードをご確認いただいたうえ、コメントをいただきありがとうございます。 本件について再度修正を行いましたのでご精査いただければ幸いです。

まず、ManagerActionListenerについては、ご指摘の通り誤ったクラスを継承し、動作しているものと勘違いしておりました。失礼いたしました。 この経緯なのですが、私の開発環境では、OpenRTM-aistの古いバージョンにも対応できるよう、まずはver.1.1.2から動作確認を行うようにしているところです。ver.1.1.2のManagerActionListenerモジュール内を確認したところ、該当のManagerActionListenerクラスが存在しなかったため、ManagerActionListenersがそうなのかなと内部もよく確認せずに誤って使用してしまいました。改めて確認したところ、 ManagerActionListenerクラスはver.1.1.2とver.1.2.0には存在せず、ver.1.2.2でのみ確認できました。ver.1.2.1は未確認です。

このことから、Python3系の通信モジュールのみ、ManagerActionListenerクラスを継承するように修正いたしました。ver.1.2.1以前のPython2系の通信モジュールについては、ManagerActionListenerクラスが存在しないことから継承はしていませんが、ManagerActionListenerクラスを新たに定義することで動作するようでしたので、そのように修正しております。これで問題ないでしょうか。

続いて、デストラクタについてはおっしゃる通りだと思います。なぜかクリーンアップはデストラクタで行うものとこだわっていました。さらに試験使用中に通信モジュールのデストラクタが呼ばれたためしがなかったため、なんとかしてデストラクタを呼ぼうとし、試行錯誤していたところです。もはやPythonのデストラクタには期待しない、ということでクリーンアップ関数を__del__関数から分離いたしました。クリーンアップ関数は、ManagerActionListenerクラスのpostShutdown関数で実行するようにしています。

以上となります。よろしくお願いいたします。

Nobu19800 commented 3 years ago

このことから、Python3系の通信モジュールのみ、ManagerActionListenerクラスを継承するように修正いたしました。ver.1.2.1以前のPython2系の通信モジュールについては、ManagerActionListenerクラスが存在しないことから継承はしていませんが、ManagerActionListenerクラスを新たに定義することで動作するようでしたので、そのように修正しております。これで問題ないでしょうか。

同じ名前のメンバ関数を定義していれば継承してなくても問題ないと思います。 1.2.0と1.1.2にManagerActionListenerクラスが無いのは知りませんでした。申し訳ありません。

他に特に問題はないと思います。修正していただいてありがとうございました。