Skip to content

Commit

Permalink
feat: VoiceModelFile::close後もidmetasへのアクセスを保証
Browse files Browse the repository at this point in the history
  • Loading branch information
qryxip committed Jan 24, 2025
1 parent 57a39ba commit c8afcda
Show file tree
Hide file tree
Showing 17 changed files with 242 additions and 44 deletions.
12 changes: 10 additions & 2 deletions crates/voicevox_core/src/voice_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,12 @@ pub(crate) mod blocking {
Inner::open(path).block_on().map(Self)
}

/// VVMファイルを閉じる。
pub fn close(self) -> (VoiceModelId, VoiceModelMeta) {
let heads = self.0.into_heads();
(*heads.header.manifest.id(), heads.header.metas.clone())
}

pub(crate) fn inner(&self) -> &Inner<SingleTasked> {
&self.0
}
Expand Down Expand Up @@ -653,8 +659,10 @@ pub(crate) mod nonblocking {
}

/// VVMファイルを閉じる。
pub async fn close(self) {
self.0.into_heads().zip.into_inner().close().await;
pub async fn close(self) -> (VoiceModelId, VoiceModelMeta) {
let heads = self.0.into_heads();
heads.zip.into_inner().close().await;
(*heads.header.manifest.id(), heads.header.metas.clone())
}

pub(crate) fn inner(&self) -> &Inner<BlockingThreadPool> {
Expand Down
30 changes: 26 additions & 4 deletions crates/voicevox_core_c_api/include/voicevox_core.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 38 additions & 12 deletions crates/voicevox_core_c_api/src/c_impls.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::{
collections::{HashMap, HashSet},
convert::Infallible,
ffi::CString,
num::NonZero,
ops::Deref,
path::Path,
ptr::NonNull,
sync::{Arc, LazyLock},
Expand All @@ -11,11 +13,11 @@ use camino::Utf8Path;
use duplicate::duplicate_item;
use easy_ext::ext;
use ref_cast::ref_cast_custom;
use voicevox_core::{Result, SpeakerMeta, VoiceModelId};
use voicevox_core::{Result, SpeakerMeta, VoiceModelId, VoiceModelMeta};

use crate::{
helpers::CApiResult,
object::{CApiObject, CApiObjectPtrExt as _},
helpers::{CApiError, CApiResult},
object::{AliveBody, Body, CApiObject, CApiObjectPtrExt as _},
OpenJtalkRc, VoicevoxInitializeOptions, VoicevoxOnnxruntime, VoicevoxSynthesizer,
VoicevoxUserDict, VoicevoxVoiceModelFile,
};
Expand Down Expand Up @@ -127,8 +129,28 @@ impl VoicevoxVoiceModelFile {

#[ext(VoicevoxVoiceModelFilePtrExt)]
impl *const VoicevoxVoiceModelFile {
pub(crate) fn ensure_opened(
self,
) -> CApiResult<impl Deref<Target = voicevox_core::blocking::VoiceModelFile>> {
let body = self.maybe_closed_body();
voicevox_core::__internal::interop::raii::try_map_guard(body, |body| match &**body {
AliveBody::Open(model) => Ok(model),
AliveBody::Closed((id, _)) => Err(CApiError::VoiceModelFileAlreadyClosed(*id)),
})
}

pub(crate) fn id(self) -> VoiceModelId {
match &*self.maybe_closed_body() {
AliveBody::Open(model) => model.id(),
AliveBody::Closed((id, _)) => *id,
}
}

pub(crate) fn metas(self) -> CString {
metas_to_json(self.body().metas())
metas_to_json(match &*self.maybe_closed_body() {
AliveBody::Open(model) => model.metas(),
AliveBody::Closed((_, metas)) => metas,
})
}
}

Expand All @@ -138,14 +160,15 @@ fn metas_to_json(metas: &[SpeakerMeta]) -> CString {
}

#[duplicate_item(
H B;
[ OpenJtalkRc ] [ voicevox_core::blocking::OpenJtalk ];
[ VoicevoxUserDict ] [ voicevox_core::blocking::UserDict ];
[ VoicevoxSynthesizer ] [ voicevox_core::blocking::Synthesizer<voicevox_core::blocking::OpenJtalk> ];
[ VoicevoxVoiceModelFile ] [ voicevox_core::blocking::VoiceModelFile ];
H O C;
[ OpenJtalkRc ] [ voicevox_core::blocking::OpenJtalk ] [ Infallible ];
[ VoicevoxUserDict ] [ voicevox_core::blocking::UserDict ] [ Infallible ];
[ VoicevoxSynthesizer ] [ voicevox_core::blocking::Synthesizer<voicevox_core::blocking::OpenJtalk> ] [ Infallible ];
[ VoicevoxVoiceModelFile ] [ voicevox_core::blocking::VoiceModelFile ] [ (VoiceModelId, VoiceModelMeta) ];
)]
impl CApiObject for H {
type RustApiObject = B;
type RustApiObject = O;
type RustApiObjectClosed = C;

fn known_addrs() -> &'static std::sync::Mutex<HashSet<NonZero<usize>>> {
static KNOWN_ADDRS: LazyLock<std::sync::Mutex<HashSet<NonZero<usize>>>> =
Expand All @@ -159,11 +182,14 @@ impl CApiObject for H {
}

fn bodies() -> &'static std::sync::Mutex<
HashMap<NonZero<usize>, Arc<parking_lot::RwLock<Option<Self::RustApiObject>>>>,
HashMap<
NonZero<usize>,
Arc<parking_lot::RwLock<Body<Self::RustApiObject, Self::RustApiObjectClosed>>>,
>,
> {
#[expect(clippy::type_complexity, reason = "`CApiObject::bodies`と同様")]
static BODIES: LazyLock<
std::sync::Mutex<HashMap<NonZero<usize>, Arc<parking_lot::RwLock<Option<B>>>>>,
std::sync::Mutex<HashMap<NonZero<usize>, Arc<parking_lot::RwLock<Body<O, C>>>>>,
> = LazyLock::new(Default::default);

&BODIES
Expand Down
5 changes: 5 additions & 0 deletions crates/voicevox_core_c_api/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ pub(crate) fn into_result_code_with_error(result: CApiResult<()>) -> VoicevoxRes
Err(InvalidAudioQuery(_)) => VOICEVOX_RESULT_INVALID_AUDIO_QUERY_ERROR,
Err(InvalidAccentPhrase(_)) => VOICEVOX_RESULT_INVALID_ACCENT_PHRASE_ERROR,
Err(InvalidUuid(_)) => VOICEVOX_RESULT_INVALID_UUID_ERROR,
Err(VoiceModelFileAlreadyClosed(_)) => {
VOICEVOX_RESULT_VOICE_MODEL_FILE_ALREADY_CLOSED_ERROR
}
}
}
}
Expand All @@ -78,6 +81,8 @@ pub(crate) enum CApiError {
InvalidAccentPhrase(serde_json::Error),
#[error("無効なUUIDです: {0}")]
InvalidUuid(uuid::Error),
#[error("音声モデルファイル`{0}`は既に閉じられています")]
VoiceModelFileAlreadyClosed(VoiceModelId),
}

pub(crate) fn audio_query_model_to_json(audio_query_model: &AudioQuery) -> String {
Expand Down
32 changes: 26 additions & 6 deletions crates/voicevox_core_c_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ pub extern "C" fn voicevox_get_version() -> *const c_char {
/// 音声モデルファイル。
///
/// VVMファイルと対応する。
/// <b>構築</b>(_construction_)は ::voicevox_voice_model_file_open で行い、<b>破棄</b>(_destruction_)は ::voicevox_voice_model_file_close で行う。
/// <b>構築</b>(_construction_)は ::voicevox_voice_model_file_open で行い、<b>破棄</b>(_destruction_)は ::voicevox_voice_model_file_delete で行う。
#[derive(Debug, Educe)]
#[educe(Default(expression = "Self { _padding: MaybeUninit::uninit() }"))]
pub struct VoicevoxVoiceModelFile {
Expand Down Expand Up @@ -462,6 +462,8 @@ pub unsafe extern "C" fn voicevox_voice_model_file_open(
// SAFETY: voicevox_core_c_apiを構成するライブラリの中に、これと同名のシンボルは存在しない
/// ::VoicevoxVoiceModelFile からIDを取得する。
///
/// ::voicevox_voice_model_file_close の後でも利用可能。
///
/// @param [in] model 音声モデル
/// @param [out] output_voice_model_id 音声モデルID
///
Expand All @@ -474,7 +476,7 @@ pub unsafe extern "C" fn voicevox_voice_model_file_id(
output_voice_model_id: NonNull<[u8; 16]>,
) {
init_logger_once();
let id = model.body().id().raw_voice_model_id().into_bytes();
let id = model.id().raw_voice_model_id().into_bytes();
unsafe { output_voice_model_id.write_unaligned(id) };
}

Expand All @@ -484,6 +486,8 @@ pub unsafe extern "C" fn voicevox_voice_model_file_id(
///
/// JSONの解放は ::voicevox_json_free で行う。
///
/// ::voicevox_voice_model_file_close の後でも利用可能。
///
/// @param [in] model 音声モデル
///
/// @returns メタ情報のJSON文字列
Expand All @@ -495,17 +499,33 @@ pub extern "C" fn voicevox_voice_model_file_create_metas_json(
C_STRING_DROP_CHECKER.whitelist(model.metas()).into_raw()
}

// TODO: cbindgenが`#[unsafe(no_mangle)]`に対応したら`#[no_mangle]`を置き換える
// SAFETY: voicevox_core_c_apiを構成するライブラリの中に、これと同名のシンボルは存在しない
/// ::VoicevoxVoiceModelFile が所有しているファイルディスクリプタを閉じる。
///
/// 対象への他スレッドでのアクセスが存在する場合、それらがすべて終わるのを待ってから閉じる。
///
/// ::voicevox_voice_model_file_delete の違いとしては、本関数が対象にした ::VoicevoxVoiceModelFile
/// に対しても ::voicevox_voice_model_file_id および
/// ::voicevox_voice_model_file_create_metas_json は利用可能である。
///
/// @param [in] model 音声モデル
#[no_mangle]
pub extern "C" fn voicevox_voice_model_file_close(model: *const VoicevoxVoiceModelFile) {
init_logger_once();
// FIXME: テストを書く
model.close_body(voicevox_core::blocking::VoiceModelFile::close);
}

// TODO: cbindgenが`#[unsafe(no_mangle)]`に対応したら`#[no_mangle]`を置き換える
// SAFETY: voicevox_core_c_apiを構成するライブラリの中に、これと同名のシンボルは存在しない
/// ::VoicevoxVoiceModelFile を、所有しているファイルディスクリプタを閉じた上で<b>破棄</b>(_destruct_)する。
///
/// 破棄対象への他スレッドでのアクセスが存在する場合、それらがすべて終わるのを待ってから破棄する。
///
/// この関数の呼び出し後に破棄し終えた対象にアクセスすると、プロセスを異常終了する。
///
/// @param [in] model 破棄対象
#[no_mangle]
pub extern "C" fn voicevox_voice_model_file_close(model: *mut VoicevoxVoiceModelFile) {
pub extern "C" fn voicevox_voice_model_file_delete(model: *mut VoicevoxVoiceModelFile) {
init_logger_once();
model.drop_body();
}
Expand Down Expand Up @@ -578,7 +598,7 @@ pub extern "C" fn voicevox_synthesizer_load_voice_model(
model: *const VoicevoxVoiceModelFile,
) -> VoicevoxResultCode {
init_logger_once();
into_result_code_with_error(synthesizer.load_voice_model(&model.body()))
into_result_code_with_error((|| synthesizer.load_voice_model(&*model.ensure_opened()?))())
}

// TODO: cbindgenが`#[unsafe(no_mangle)]`に対応したら`#[no_mangle]`を置き換える
Expand Down
Loading

0 comments on commit c8afcda

Please sign in to comment.