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 8429882
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 26 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
16 changes: 14 additions & 2 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.

23 changes: 14 additions & 9 deletions crates/voicevox_core_c_api/src/c_impls.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{
collections::{HashMap, HashSet},
convert::Infallible,
ffi::CString,
num::NonZero,
path::Path,
Expand All @@ -15,7 +16,7 @@ use voicevox_core::{Result, SpeakerMeta, VoiceModelId};

use crate::{
helpers::CApiResult,
object::{CApiObject, CApiObjectPtrExt as _},
object::{Body, CApiObject, CApiObjectPtrExt as _},
OpenJtalkRc, VoicevoxInitializeOptions, VoicevoxOnnxruntime, VoicevoxSynthesizer,
VoicevoxUserDict, VoicevoxVoiceModelFile,
};
Expand Down Expand Up @@ -138,14 +139,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 ] [ Infallible ];
)]
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 +161,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
17 changes: 15 additions & 2 deletions crates/voicevox_core_c_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,15 +497,28 @@ pub extern "C" fn voicevox_voice_model_file_create_metas_json(

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

// 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_delete(model: *mut VoicevoxVoiceModelFile) {
init_logger_once();
model.drop_body();
}
Expand Down
85 changes: 78 additions & 7 deletions crates/voicevox_core_c_api/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use std::{
any,
collections::{HashMap, HashSet},
convert::Infallible,
fmt::{Debug, Display},
mem,
num::NonZero,
Expand All @@ -16,6 +17,7 @@ use std::{
};

use easy_ext::ext;
use itertools::Either;
use tracing::warn;

// FIXME: 次のような状況に備え、`new`をいっぱい行うテストを書く
Expand All @@ -40,6 +42,7 @@ use tracing::warn;
/// 3. そもそもオブジェクトとして変なダングリングポインタが渡される
pub(crate) trait CApiObject: Default + Debug + 'static {
type RustApiObject: 'static;
type RustApiObjectClosed: 'static;

// 行う可変操作は`insert`のみ
fn known_addrs() -> &'static std::sync::Mutex<HashSet<NonZero<usize>>>;
Expand All @@ -49,11 +52,7 @@ pub(crate) trait CApiObject: Default + Debug + 'static {
fn bodies() -> &'static std::sync::Mutex<
HashMap<
NonZero<usize>, // `heads`の要素へのポインタのアドレス
Arc<
parking_lot::RwLock<
Option<Self::RustApiObject>, // `RwLock`をdropする直前まで`Some`
>,
>,
Arc<parking_lot::RwLock<Body<Self::RustApiObject, Self::RustApiObjectClosed>>>,
>,
>;

Expand Down Expand Up @@ -82,7 +81,10 @@ impl<T: CApiObject> *const T {
/// # Panics
///
/// 同じ対象に対して`drop_body`を呼んでいるとパニックする。
pub(crate) fn body(self) -> impl Deref<Target = T::RustApiObject> {
pub(crate) fn body(self) -> impl Deref<Target = T::RustApiObject>
where
T: CApiObject<RustApiObjectClosed = Infallible>,
{
let this = self.validate();

let body = T::lock_bodies()
Expand All @@ -96,6 +98,29 @@ impl<T: CApiObject> *const T {
.unwrap_or_else(|()| this.panic_for_deleted())
}

/// # Panics
///
/// 同じ対象に対して`drop_body`を呼んでいるとパニックする。
pub(crate) fn body_(
self,
) -> impl Deref<Target = AliveBody<T::RustApiObject, T::RustApiObjectClosed>>
where
T: CApiObject<RustApiObjectClosed = Infallible>,
{
let this = self.validate();

let body = T::lock_bodies()
.get(&this.addr())
.unwrap_or_else(|| this.panic_for_deleted())
.read_arc();

voicevox_core::__internal::interop::raii::try_map_guard(body, |body| match &**body {
Body::Alive(x) => Ok(x),
Body::Deleting => Err(()),
})
.unwrap_or_else(|()| this.panic_for_deleted())
}

/// # Panics
///
/// 同じ対象に対してこの関数を二度呼ぶとパニックする。
Expand Down Expand Up @@ -154,8 +179,54 @@ impl<T: CApiObject> T {
}

fn lock_bodies() -> impl DerefMut<
Target = HashMap<NonZero<usize>, Arc<parking_lot::RwLock<Option<Self::RustApiObject>>>>,
Target = HashMap<
NonZero<usize>,
Arc<parking_lot::RwLock<Body<Self::RustApiObject, Self::RustApiObjectClosed>>>,
>,
> {
Self::bodies().lock().unwrap_or_else(|e| panic!("{e}"))
}
}

pub(crate) enum Body<O, C> {
Alive(AliveBody<O, C>),
Deleting,
}

impl<O, C> Body<O, C> {
fn alive(&self) -> Option<&AliveBody<O, C>> {
match self {
Body::Alive(x) => Some(x),
Body::Deleting => None,
}
}

fn take(&mut self) -> Option<impl Sized> {
match mem::replace(self, Self::Deleting) {
Body::Alive(AliveBody::Open(x)) => Some(Either::Right(x)),
Body::Alive(AliveBody::Closed(x)) => Some(Either::Left(x)),
Body::Deleting => None,
}
}
}

impl<O> Body<O, Infallible> {
fn as_ref(&self) -> Option<&O> {
match self {
Body::Alive(AliveBody::Open(x)) => Some(x),
Body::Alive(AliveBody::Closed(_)) => unreachable!(),
Body::Deleting => None,
}
}
}

impl<O, C> From<O> for Body<O, C> {
fn from(x: O) -> Self {
Self::Alive(AliveBody::Open(x))
}
}

pub(crate) enum AliveBody<O, C> {
Open(O),
Closed(C),
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@ public class VoiceModelFile implements Closeable {

private long handle;

/** ID。 */
/**
* ID。
*
* <p>{@link #close}の後でも利用可能。
*/
@Nonnull public final UUID id;

/** メタ情報。 */
/**
* メタ情報。
*
* <p>{@link #close}の後でも利用可能。
*/
@Nonnull public final SpeakerMeta[] metas;

public VoiceModelFile(String modelPath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,20 @@ class VoiceModelFile:
...
@property
def id(self) -> VoiceModelId:
"""ID。"""
"""
ID。
:attr:`close` および :attr:`__aexit__` の後でも利用可能。
"""
...
@property
def metas(self) -> list[SpeakerMeta]:
"""
メタ情報。
この中身を書き換えても、 ``VoiceModelFile`` としての動作には影響しない。
:attr:`close` および :attr:`__aexit__` の後でも利用可能。
"""
...
async def __aenter__(self) -> "VoiceModelFile": ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,20 @@ class VoiceModelFile:
...
@property
def id(self) -> VoiceModelId:
"""ID。"""
"""
ID。
:attr:`close` および :attr:`__exit__` の後でも利用可能。
"""
...
@property
def metas(self) -> list[SpeakerMeta]:
"""
メタ情報。
この中身を書き換えても、 ``VoiceModelFile`` としての動作には影響しない。
:attr:`close` および :attr:`__exit__` の後でも利用可能。
"""
...
def __enter__(self) -> "VoiceModelFile": ...
Expand Down

0 comments on commit 8429882

Please sign in to comment.