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

[project-vvm-async-api] "buffer"をRustの世界で保持し続ける #525

Conversation

qryxip
Copy link
Member

@qryxip qryxip commented Jun 15, 2023

内容

BufferManagerに対するリファクタです。

  • BufferManagerLayoutを持つのをやめ、UnsafeCellBox<[u8]>/CStringを直接保持し続けます。

    Feature/c api cut memcpy #392 (comment)の考えです。

    Rustの世界でowned型として保持しながらCの世界に*mut _で貸し出すわけにはいかないというのもあって選択肢から外してしまったのですが、UnsafeCellで包んでしまえば(多分)大丈夫なことに気づいてしまいました。

    これによりvoicevox_wav_free/voicevox_json_freeはRustの世界でunsafeではなくなります。デストラクトするのにUnsafeCellをただdropするだけでよくなるからです。

  • BufferManagerを解体し、SliceOwner<T>CStringOwnerに分解します。

    [u8]CStrを別物として扱い始めた時点で、役割は分担可能だったのだからこうすべきでした。

    名前としては"owner"としました。何故なら正真正銘バッファをown(所有)しているからです。C側にはポインタを参照(reference)として貸し出し(lend)ます。

関連 Issue

その他

正直なところ私自身C FFIへの経験/知識が薄く、手探りでやっている部分が大きいです。今回も #392 の時点でここまでわかっていれば...との思いがあります。

@qryxip qryxip marked this pull request as ready for review June 15, 2023 17:39
@qryxip
Copy link
Member Author

qryxip commented Jun 15, 2023

とりあえずdocを書いたのでdraftを外します。

Copy link
Member

@Hiroshiba Hiroshiba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ほぼLGTMです!

crates/voicevox_core_c_api/src/owners/slice.rs Outdated Show resolved Hide resolved
crates/voicevox_core_c_api/src/owners/slice.rs Outdated Show resolved Hide resolved
crates/voicevox_core_c_api/src/owners/slice.rs Outdated Show resolved Hide resolved
@qryxip
Copy link
Member Author

qryxip commented Jun 16, 2023

すみません、二転三転して申し訳ないのですが、CStringOwnerについては別の形がいいかなと思い始めました。

というのもCStr/CStringのドキュメントを見たところ、「Rustの世界のオブジェクトとして生存したまま可能な可変操作」が一切提供されていないことに気付いてしまいました。つまり"正しく"可変操作するためにはCString::into_raw/CString::from_rawを経由するしかありません。
(標準ライブラリの実装上はCStr[c_char]に等しいため今のところ問題は考えられないのですが、Unsafe Rustには「正式にドキュメント化されていない内部実装の性質」に頼ってはいけないという鉄則があります)

そこで回避策を考えているうちに思ったのですが、CStringに関してはそもそも"owner"とか"manager"といった枠組みで考えないほうがいいかもしれません。

今owner.rsには"owner"の必要性について長々と書いていると思うのですが、CStringはよく考えたら関係ありません。長さをこちら側で管理する必要は無くポインタ一つで実体を表すことができるので、本来は以下のような形で十分なはずです。

#[no_mangle]
pub extern "C" fn new_string() -> *mut c_char {
    CString::new("Hello!").unwrap().into_raw() // `into_raw`でownershipをC側に移し…
}

/// # Safety
///
/// - `s`は`new_string`で得たものである。
#[no_mangle]
pub unsafe extern "C" fn delete_string(s: *mut c_char) {
    drop(CString::from_raw(s)); // C側から戻ってきたownershipを受け取って`from_raw`
}

それができない理由は我々が #500 をやりたいから、それだけです。であるならユーザーに与える文字列そのものは"own"も"manage"もせずに「間違えた解放に対する拒否」ができればいいはずです。CStringDropCheckerとかVerifierとかAuditorとかWhiteBlackListみたいな名前の構造体にすれば丸く収まり、責務も簡潔になるのではないかと思いました。

(追記) そもそもこの部分が要らず、今となってはvoicevox_error_result_to_messageの文字列だけ弾ければいいかもしれません。考えられる懸念は#370 (comment)くらいのもので、二重解放やvoicevox_coreに関係無いポインタの投入はケアしなくていのかも... そしたら構造体も要らずvoicevox_json_free内でベタ書きできるんじゃないかと。

panic!(
"解放しようとしたポインタはvoicevox_coreの管理下にありません。\
誤ったポインタであるか、二重解放になっていることが考えられます",
);

@Hiroshiba
Copy link
Member

@qryxip なるほどです! CStringOwnerを不要にできるのは大きいですね!
CStringDropCheckerにするの、良い気がします。
owner.rsが不要になってSliceOwnerだけになるのでドキュメントも簡単になりますし。

voicevox_error_result_to_messageのCStringをブラックリストとして持っておく手もなるほどと思いました。
ただ新しくそういうCStringが現れた時におそらく追加し忘れるオチだろうな~と思いました。
ブラックリストを保持するクラスを作るのと、CStringDropCheckerを作るのはそこまで手間変わらなそうですし、こっちのが良さそうかも。

Copy link
Member Author

@qryxip qryxip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CStringDropCheckerにしました。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あくまでチェックということで、MaybeUninitへの書き込みとCString::{from,into}_rawをここから外に出しました。

メソッド名および概念は"whitelist"(動詞兼名詞)と"blacklist"(動詞兼名詞)としてみました。

crates/voicevox_core_c_api/src/lib.rs Outdated Show resolved Hide resolved
Comment on lines +22 to +23
/// Cの世界に`CString`を送り出す前に`whitelist`を通し、戻って来た`*mut c_char`を`CString`にしてdrop
/// する前に`check`に通す。
Copy link
Member

@Hiroshiba Hiroshiba Jun 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

将来なにか関数増えたときにcheck通すの忘れそうですね。。
仕組みで解決できないので、まあ思い出すしか無さそう。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ですね... ベタ書きしたものをCの世界に向けて発射できる以上、どうしようもない気がします。

Copy link
Member

@Hiroshiba Hiroshiba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!!!!

見通しが上がったように感じます、ありがとうございます!!

@Hiroshiba Hiroshiba merged commit a00ff98 into VOICEVOX:project-vvm-async-api Jun 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants