Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dllアンロード後"char.bin"のロックが残る件について #17

Open
2 tasks
miyabin1701 opened this issue Jan 28, 2024 · 5 comments
Open
2 tasks

Comments

@miyabin1701
Copy link

内容

こんばんは、voicevox-core.dllをアンロードしても辞書ファイル"char.bin"のロックが
外れないので悩んでします・。

実行時の読み込まれてるdllリスト
Base Size Path
0x00000000be220000 0x62000 R:\release\qtMeCabon.exe
0x0000000067600000 0xb5000 R:\release\portaudio_x64.dll
0x00000000cb360000 0x255000 R:\release\voicevox_core.dll
0x00000000ca6b0000 0x494000 R:\release\libmecab64.dll

dllをFreeLibraryでアンロードしました。
0x00000000be220000 0x62000 R:\release\qtMeCabon.exe
0x0000000067600000 0xb5000 R:\release\portaudio_x64.dll

 正常にdllはアンロードされましたが、mecabとvoicevox-openjtalk-mecabの開いている
"char.bin"ファイルが開かれたままになっていて、辞書のビルドをしようとするとパーミッションエラーになってしまいます。
windows10になってからなのか?dllが開きっぱなしのファイルは親プロセスが終了するまでロックが解除されないようです。

 mecab側で試してみましたが、FreeLibraryの前に使っていたlattice,mecab,modelを
明示的にdestroyするとロックが外れることが分かりました。

mecab_lattice_destroy( lattice );
mecab_destroy( mecab );
mecab_model_destroy( model );
if ( FreeLibrary( hMecabDLL ) == 0 )

voicevox_finalize()の中で、mecabの上記後始末関数呼んで頂ければ助かります。
現在 voicevox_core-0.14.5 を使っています。よろしくお願いします。

Pros 良くなる点

dllをアンロード後にmecabの辞書がビルド可能になります。

Cons 悪くなる点

ソースの行数が少し増える?1行??

実現方法

voicevox_finalize呼び出し時にmecab_destroy( mecab );を呼ぶ

VOICEVOXのバージョン

-core 0.14.5

OSの種類/ディストリ/バージョン

  • [x ] Windows
  • macOS
  • Linux

その他

dllの実行中ロードやアンロードのテストコードを書くには時間がかかるでしょうから修正個所を教えていただければこちらでテストします。または、こちらのソースを先にGitHubにアップする方がよろしければそうします。

@miyabin1701
Copy link
Author

qtmeecabon-charbin009-APPrun-build
char.binを開いているアプリはリソースモニターで確認できます。上記はmecabとvoicevoxがロックを外してない状態で、辞書をビルドしている(この時は別のフォルダーのchar.binを作成中で、後のステップでロックされているフォルダーの更新しようとしてこけます)際のものです。

@PickledChair
Copy link
Member

PickledChair commented Jan 29, 2024

ご報告ありがとうございます!

Discordでのやり取りの後、この件についてもう少し調べてみたのですが、自分の当初の予想とは異なる考えになりました。結論から言うと、問題は open_jtalk-rs の実装にはなく voicevox_core の方の問題のようでした。かつ、voicevox_coreの main ブランチではライブラリの設計変更に伴ってこの問題がすでに解決されている可能性が高そうでした。

問題が解決したバージョンのリリースにはまだ少し時間がかかると思います(次回リリースのバージョン 0.15 はハミング機能(project-s)の成果の投入が優先されています)。その後のバージョン 0.16 では解決しているのではないかと思います。

混乱させてしまっていたらすみません。以下でもう少し詳しく説明します。

Q. デストラクタで mecab_destroy 関数の呼び出しをするべきか

A. しなくても良い。というより呼び出しできない

OpenJTalk 版の MeCab には OpenJTalk 用の API が後付けされているようでした("for Open JTalk" と書かれています)。MeCab のオリジナルの API には "old mecab interface" と書かれています。

この2つの系統の API は別々のライフサイクルを持っているようです。非常に簡略に書くと以下の感じになりそうです:

  • MeCab 版の API (old mecab interface)
    • mecab_new 関数でインスタンス作成
    • mecab_destroy 関数でインスタンスの破棄
  • OpenJTalk 版の API (for Open JTalk)
    • Mecab_initialize 関数で初期化
    • Mecab_clear 関数で解放処理

両系統の API の間では扱う構造体に互換性がないので、両者を混ぜて使うことはできません。そして open_jtalk-rs では後者の OpenJTalk 版の API を用いています。したがって、デストラクタでは Mecab_clear のみを呼び出せば良いということになります。

Q. では、voicevox_core をアンロードしても辞書ファイルのロックが外れなかったのはなぜか?

A. voicevox_core の実装が、open_jtalk-rs のデストラクタを呼ばない書き方になっていたから

設計変更前の voicevox_core では、ライブラリの単一の状態を static 変数で保持していました。static 変数に束縛されているオブジェクトはプログラムの終了時であってもデストラクタが呼ばれません(Rust の static のドキュメントで以下のように説明されています。drop が Rust におけるデストラクタです)。

Static items do not call drop at the end of the program.

一方、新しい設計の voicevox_core では、OpenJTalk の機能は OpenJtalkRc という構造体を介して使うようになっています。この構造体は static 変数には束縛されず、ライブラリユーザーが API を介して明示的にリソースを管理するようになっているので、同じ問題は起きません。

Windows 向けのサンプルコードが以下にあります:

https://github.com/VOICEVOX/voicevox_core/blob/b9c953c56081d569b5dce4ed5aea3f6f79976ba6/example/cpp/windows/simple_tts/simple_tts.cpp#L37-L48

voicevox_open_jtalk_rc_new 関数でインスタンス作成、voicevox_open_jtalk_rc_delete 関数で削除処理をします。比較的早くに(まだその後も使われていそうなのに)voicevox_open_jtalk_rc_delete 関数を呼び出していて不思議に思うかもしれませんが、これはインスタンスが参照カウントで管理されているからです(Rc というのは reference counted のこと)。最初のインスタンス生成でカウント1、voicevox_synthesizer_newopen_jtalk を渡した時にカウント2になり、API 呼び出し側のスコープではその後 open_jtalk を使う予定がないので voicevox_open_jtalk_rc_delete でカウントをマイナス1しています。最終的には voicevox_synthesizer_delete 関数の呼び出しの時に、VoicevoxSynthesizer 構造体の破棄と同時にカウント0となり、リソースが解放されるはずです。

(余談ですが、ユーザー辞書 API も追加されたので、今後はユーザー独自の辞書作成・操作もサポートされるようになります。)

@miyabin1701
Copy link
Author

本文の方有難うございます。できれば0.14.5でロック解除できるとありがたいのですが。余談の方にコメントすると
拙作では、システム辞書を更新する方向で進めています。何故なら、辞書には登録されていても、適切に読めないことが多発しているからです。コーパスファイルに追記して、システム辞書をビルドしてmecabのmodelファイルを調整するしか適切に読めるように導く方法は無いと思います。これは、小説などの長文を読み上げさせようとしている場合の話ですが。主戦場を「あるじせんじょう」と読んだり、大規模を「おおきぼ」と読んだりします。mecabのmodelファイルはmecab-ipadic-2.7.0-20070801U8.modelしか見つからなかったのでそれを元に、読み損じをコーパスファイルに加筆しています。
ただ、どれだけの例をコーパスファイルに書けば「行った」が「いった」なのか「おこなった」なのかを区別できるようになるかどうかわかりません。左の文字が「に、へ」がいったで「を」がおこなった、「が」のときはどちらかわからない?
ユーザ辞書登録はその語彙が一つの読み方しかない場合には有効かも知れません。
一番早いのはソースの文章をかなに変更することでしょうか・・・アクセントがつかないかもしれないけど

@PickledChair
Copy link
Member

できれば0.14.5でロック解除できるとありがたいのですが

0.14 系は、0.15 が近日リリースされるためサポート対象外となると思います。一方、上述したように 0.15 でも問題は解決されていませんが、API の変更は(非公開 API が追加された以外は)ないと思うので、早期の修正が必要であれば 0.15 系で修正し、こちらをお使いいただくのが良いと思います(恐らく修正できるとは思うのですが、まだ修正方法を深くは検討していません)。

ただ、0.16 が早いうちにリリースされることになれば、これは問題解決済みのバージョンのはずなので 0.16 を待っていただくのが良い気もしています。

@Hiroshiba 0.15 系のサポート中に修正するか、0.16 系を待つかは 0.16 のリリース時期次第だと思うので、私には判断できませんでした(最近時間が取れず開発状況を完全にはキャッチアップできていなくてすみません)。こちらの判断をお願いしてもよろしいでしょうか?

余談の方にコメントすると
拙作では、システム辞書を更新する方向で進めています。

なるほどです。これであれば、確かにファイルロックの問題は致命的ですね。

@Hiroshiba
Copy link
Member

@PickledChair 連絡ありがとうございます!!
最新版では解決済みなんですね!!よかった。

@miyabin1701
追従が大変かもなのですが、最新版をおすすめします!!
どうしてもであればrelease-0.15ブランチにプルリクエストをお願いします 🙏
他にはまあプロセスごと分けちゃえるエンジンの方を使う手もあるかも・・・?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants