diff --git a/crates/voicevox_core_c_api/src/helpers.rs b/crates/voicevox_core_c_api/src/helpers.rs index d549959f9..e740d12a7 100644 --- a/crates/voicevox_core_c_api/src/helpers.rs +++ b/crates/voicevox_core_c_api/src/helpers.rs @@ -1,5 +1,5 @@ use std::alloc::Layout; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Debug; use thiserror::Error; @@ -232,25 +232,38 @@ impl Default for VoicevoxSynthesisOptions { // libcのmallocで追加のアロケーションを行うことなく、`Vec`や`Vec`の内容を直接Cの世界に貸し出す。 /// Rustの世界の`Box<[impl Copy]>`をCの世界に貸し出すため、アドレスとレイアウトを管理するもの。 -pub(crate) struct BufferManager { +/// +/// `Mutex`による内部可変性を持ち、すべての操作は`&self`から行うことができる。 +pub(crate) struct BufferManager(Mutex); + +struct BufferManagerInner { address_to_layout_table: BTreeMap, + owned_str_addrs: BTreeSet, + static_str_addrs: BTreeSet, } impl BufferManager { pub const fn new() -> Self { - Self { + Self(Mutex::new(BufferManagerInner { address_to_layout_table: BTreeMap::new(), - } + owned_str_addrs: BTreeSet::new(), + static_str_addrs: BTreeSet::new(), + })) } - pub fn vec_into_raw(&mut self, vec: Vec) -> (*mut T, usize) { + pub fn vec_into_raw(&self, vec: Vec) -> (*mut T, usize) { + let BufferManagerInner { + address_to_layout_table, + .. + } = &mut *self.0.lock().unwrap(); + let slice = Box::leak(vec.into_boxed_slice()); let layout = Layout::for_value(slice); let len = slice.len(); let ptr = slice.as_mut_ptr(); let addr = ptr as usize; - let not_occupied = self.address_to_layout_table.insert(addr, layout).is_none(); + let not_occupied = address_to_layout_table.insert(addr, layout).is_none(); assert!(not_occupied, "すでに値が入っている状態はおかしい"); @@ -262,9 +275,14 @@ impl BufferManager { /// # Safety /// /// - `buffer_ptr`は`vec_into_raw`で取得したものであること。 - pub unsafe fn dealloc_slice(&mut self, buffer_ptr: *const T) { + pub unsafe fn dealloc_slice(&self, buffer_ptr: *const T) { + let BufferManagerInner { + address_to_layout_table, + .. + } = &mut *self.0.lock().unwrap(); + let addr = buffer_ptr as usize; - let layout = self.address_to_layout_table.remove(&addr).expect( + let layout = address_to_layout_table.remove(&addr).expect( "解放しようとしたポインタはvoicevox_coreの管理下にありません。\ 誤ったポインタであるか、二重解放になっていることが考えられます", ); @@ -278,6 +296,53 @@ impl BufferManager { std::alloc::dealloc(addr as *mut u8, layout); } } + + pub fn c_string_into_raw(&self, s: CString) -> *mut c_char { + let BufferManagerInner { + owned_str_addrs, .. + } = &mut *self.0.lock().unwrap(); + + let ptr = s.into_raw(); + owned_str_addrs.insert(ptr as _); + ptr + } + + /// `c_string_into_raw`でC API利用側に貸し出したポインタに対し、デアロケートする。 + /// + /// # Safety + /// + /// - `ptr`は`c_string_into_raw`で取得したものであること。 + pub unsafe fn dealloc_c_string(&self, ptr: *mut c_char) { + let BufferManagerInner { + owned_str_addrs, + static_str_addrs, + .. + } = &mut *self.0.lock().unwrap(); + + if !owned_str_addrs.remove(&(ptr as _)) { + if static_str_addrs.contains(&(ptr as _)) { + panic!( + "解放しようとしたポインタはvoicevox_core管理下のものですが、\ + voicevox_coreがアンロードされるまで永続する文字列に対するものです。\ + 解放することはできません", + ) + } + panic!( + "解放しようとしたポインタはvoicevox_coreの管理下にありません。\ + 誤ったポインタであるか、二重解放になっていることが考えられます", + ); + } + drop(CString::from_raw(ptr)); + } + + pub fn memorize_static_str(&self, ptr: *const c_char) -> *const c_char { + let BufferManagerInner { + static_str_addrs, .. + } = &mut *self.0.lock().unwrap(); + + static_str_addrs.insert(ptr as _); + ptr + } } #[cfg(test)] @@ -320,11 +385,37 @@ mod tests { #[should_panic( expected = "解放しようとしたポインタはvoicevox_coreの管理下にありません。誤ったポインタであるか、二重解放になっていることが考えられます" )] - fn buffer_manager_denies_unknown_ptr() { - let mut buffer_manager = BufferManager::new(); + fn buffer_manager_denies_unknown_slice_ptr() { + let buffer_manager = BufferManager::new(); unsafe { let x = 42; buffer_manager.dealloc_slice(&x as *const i32); } } + + #[test] + #[should_panic( + expected = "解放しようとしたポインタはvoicevox_coreの管理下にありません。誤ったポインタであるか、二重解放になっていることが考えられます" + )] + fn buffer_manager_denies_unknown_char_ptr() { + let buffer_manager = BufferManager::new(); + unsafe { + let s = CStr::from_bytes_with_nul(b"\0").unwrap().to_owned(); + buffer_manager.dealloc_c_string(s.into_raw()); + } + } + + #[test] + #[should_panic( + expected = "解放しようとしたポインタはvoicevox_core管理下のものですが、voicevox_coreがアンロードされるまで永続する文字列に対するものです。解放することはできません" + )] + fn buffer_manager_denies_known_static_char_ptr() { + let buffer_manager = BufferManager::new(); + unsafe { + buffer_manager.memorize_static_str(STATIC.as_ptr() as _); + buffer_manager.dealloc_c_string(STATIC.as_ptr() as *mut c_char); + } + + static STATIC: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") }; + } } diff --git a/crates/voicevox_core_c_api/src/lib.rs b/crates/voicevox_core_c_api/src/lib.rs index 32bb3ec3a..f4df35d09 100644 --- a/crates/voicevox_core_c_api/src/lib.rs +++ b/crates/voicevox_core_c_api/src/lib.rs @@ -67,7 +67,7 @@ pub(crate) fn lock_internal() -> MutexGuard<'static, Internal> { } // C_APIに渡すために,VecやCStringのサイズを記憶しながら生ポインタを得るためのマネージャ -static BUFFER_MANAGER: Mutex = Mutex::new(BufferManager::new()); +static BUFFER_MANAGER: BufferManager = BufferManager::new(); /* * Cの関数として公開するための型や関数を定義するこれらの実装はvoicevox_core/publish.rsに定義してある対応する関数にある @@ -131,7 +131,7 @@ static VOICEVOX_VERSION: once_cell::sync::Lazy = /// @return SemVerでフォーマットされたバージョン #[no_mangle] pub extern "C" fn voicevox_get_version() -> *const c_char { - VOICEVOX_VERSION.as_ptr() + BUFFER_MANAGER.memorize_static_str(VOICEVOX_VERSION.as_ptr()) } /// モデルを読み込む @@ -166,14 +166,14 @@ pub extern "C" fn voicevox_finalize() { /// @return メタ情報のjson文字列 #[no_mangle] pub extern "C" fn voicevox_get_metas_json() -> *const c_char { - lock_internal().get_metas_json().as_ptr() + BUFFER_MANAGER.memorize_static_str(lock_internal().get_metas_json().as_ptr()) } /// サポートデバイス情報をjsonで取得する /// @return サポートデバイス情報のjson文字列 #[no_mangle] pub extern "C" fn voicevox_get_supported_devices_json() -> *const c_char { - lock_internal().get_supported_devices_json().as_ptr() + BUFFER_MANAGER.memorize_static_str(lock_internal().get_supported_devices_json().as_ptr()) } /// 音素ごとの長さを推論する @@ -201,7 +201,7 @@ pub unsafe extern "C" fn voicevox_predict_duration( std::slice::from_raw_parts_mut(phoneme_vector, length), speaker_id, )?; - let (ptr, size) = BUFFER_MANAGER.lock().unwrap().vec_into_raw(output_vec); + let (ptr, size) = BUFFER_MANAGER.vec_into_raw(output_vec); output_predict_duration_data_length.write(size); output_predict_duration_data.write(ptr); @@ -217,10 +217,7 @@ pub unsafe extern "C" fn voicevox_predict_duration( /// @param predict_duration_data voicevox_predict_durationで確保されたポインタであり、かつ呼び出し側でバッファの変更が行われていないこと #[no_mangle] pub unsafe extern "C" fn voicevox_predict_duration_data_free(predict_duration_data: *mut f32) { - BUFFER_MANAGER - .lock() - .unwrap() - .dealloc_slice(predict_duration_data as *const f32); + BUFFER_MANAGER.dealloc_slice(predict_duration_data as *const f32); } /// モーラごとのF0を推論する @@ -269,7 +266,7 @@ pub unsafe extern "C" fn voicevox_predict_intonation( std::slice::from_raw_parts(end_accent_phrase_vector, length), speaker_id, )?; - let (ptr, len) = BUFFER_MANAGER.lock().unwrap().vec_into_raw(output_vec); + let (ptr, len) = BUFFER_MANAGER.vec_into_raw(output_vec); output_predict_intonation_data.write(ptr); output_predict_intonation_data_length.write(len); @@ -285,10 +282,7 @@ pub unsafe extern "C" fn voicevox_predict_intonation( /// @param predict_duration_data voicevox_predict_intonationで確保された,ポインタでありかつ,呼び出し側でバッファの変更を行われていないこと. #[no_mangle] pub unsafe extern "C" fn voicevox_predict_intonation_data_free(predict_intonation_data: *mut f32) { - BUFFER_MANAGER - .lock() - .unwrap() - .dealloc_slice(predict_intonation_data as *const f32); + BUFFER_MANAGER.dealloc_slice(predict_intonation_data as *const f32); } /// decodeを実行する @@ -324,7 +318,7 @@ pub unsafe extern "C" fn voicevox_decode( std::slice::from_raw_parts(phoneme_vector, phoneme_size * length), speaker_id, )?; - let (ptr, len) = BUFFER_MANAGER.lock().unwrap().vec_into_raw(output_vec); + let (ptr, len) = BUFFER_MANAGER.vec_into_raw(output_vec); output_decode_data.write(ptr); output_decode_data_length.write(len); Ok(()) @@ -338,7 +332,7 @@ pub unsafe extern "C" fn voicevox_decode( /// @param decode_data voicevox_decodeで確保されたポインタであり、かつ呼び出し側でバッファの変更を行われていないこと #[no_mangle] pub unsafe extern "C" fn voicevox_decode_data_free(decode_data: *mut f32) { - BUFFER_MANAGER.lock().unwrap().dealloc_slice(decode_data); + BUFFER_MANAGER.dealloc_slice(decode_data); } /// Audio query のオプション @@ -376,7 +370,7 @@ pub unsafe extern "C" fn voicevox_audio_query( let text = CStr::from_ptr(text); let audio_query = create_audio_query(text, speaker_id, Internal::audio_query, options)?; - output_audio_query_json.write(audio_query.into_raw()); + output_audio_query_json.write(BUFFER_MANAGER.c_string_into_raw(audio_query)); Ok(()) })()) } @@ -417,7 +411,7 @@ pub unsafe extern "C" fn voicevox_accent_phrases( let accent_phrases = create_accent_phrases(text, speaker_id, Internal::accent_phrases, options)?; - output_accent_phrases_json.write(accent_phrases.into_raw()); + output_accent_phrases_json.write(BUFFER_MANAGER.c_string_into_raw(accent_phrases)); Ok(()) })()) } @@ -447,7 +441,8 @@ pub unsafe extern "C" fn voicevox_mora_length( let accent_phrases_with_mora_length = modify_accent_phrases(&accent_phrases, speaker_id, Internal::mora_length)?; - output_accent_phrases_json.write(accent_phrases_with_mora_length.into_raw()); + output_accent_phrases_json + .write(BUFFER_MANAGER.c_string_into_raw(accent_phrases_with_mora_length)); Ok(()) })()) } @@ -477,7 +472,8 @@ pub unsafe extern "C" fn voicevox_mora_pitch( let accent_phrases_with_mora_pitch = modify_accent_phrases(&accent_phrases, speaker_id, Internal::mora_pitch)?; - output_accent_phrases_json.write(accent_phrases_with_mora_pitch.into_raw()); + output_accent_phrases_json + .write(BUFFER_MANAGER.c_string_into_raw(accent_phrases_with_mora_pitch)); Ok(()) })()) } @@ -507,7 +503,8 @@ pub unsafe extern "C" fn voicevox_mora_data( let accent_phrases_with_mora_data = modify_accent_phrases(&accent_phrases, speaker_id, Internal::mora_data)?; - output_accent_phrases_json.write(accent_phrases_with_mora_data.into_raw()); + output_accent_phrases_json + .write(BUFFER_MANAGER.c_string_into_raw(accent_phrases_with_mora_data)); Ok(()) })()) } @@ -553,7 +550,7 @@ pub unsafe extern "C" fn voicevox_synthesis( &serde_json::from_str(audio_query_json).map_err(CApiError::InvalidAudioQuery)?; let wav = lock_internal().synthesis(audio_query, speaker_id, options.into())?; - let (ptr, len) = BUFFER_MANAGER.lock().unwrap().vec_into_raw(wav); + let (ptr, len) = BUFFER_MANAGER.vec_into_raw(wav); output_wav.write(ptr); output_wav_length.write(len); @@ -599,7 +596,7 @@ pub unsafe extern "C" fn voicevox_tts( into_result_code_with_error((|| { let text = ensure_utf8(CStr::from_ptr(text))?; let output = lock_internal().tts(text, speaker_id, options.into())?; - let (ptr, size) = BUFFER_MANAGER.lock().unwrap().vec_into_raw(output); + let (ptr, size) = BUFFER_MANAGER.vec_into_raw(output); output_wav.write(ptr); output_wav_length.write(size); Ok(()) @@ -613,7 +610,7 @@ pub unsafe extern "C" fn voicevox_tts( /// @param voicevox_audio_query で確保されたポインタであり、かつ呼び出し側でバッファの変更を行われていないこと #[no_mangle] pub unsafe extern "C" fn voicevox_audio_query_json_free(audio_query_json: *mut c_char) { - drop(CString::from_raw(audio_query_json)); + BUFFER_MANAGER.dealloc_c_string(audio_query_json); } /// jsonフォーマットされた AccnetPhrase データのメモリを解放する @@ -623,7 +620,7 @@ pub unsafe extern "C" fn voicevox_audio_query_json_free(audio_query_json: *mut c /// @param voicevox_accent_phrases で確保されたポインタであり、かつ呼び出し側でバッファの変更を行われていないこと #[no_mangle] pub unsafe extern "C" fn voicevox_accent_phrases_json_free(accented_phrase_json: *mut c_char) { - drop(CString::from_raw(accented_phrase_json)); + BUFFER_MANAGER.dealloc_c_string(accented_phrase_json); } /// wav データのメモリを解放する @@ -633,7 +630,7 @@ pub unsafe extern "C" fn voicevox_accent_phrases_json_free(accented_phrase_json: /// @param wav voicevox_tts,voicevox_synthesis で確保されたポインタであり、かつ呼び出し側でバッファの変更を行われていないこと #[no_mangle] pub unsafe extern "C" fn voicevox_wav_free(wav: *mut u8) { - BUFFER_MANAGER.lock().unwrap().dealloc_slice(wav); + BUFFER_MANAGER.dealloc_slice(wav); } /// エラー結果をメッセージに変換する @@ -643,7 +640,9 @@ pub unsafe extern "C" fn voicevox_wav_free(wav: *mut u8) { pub extern "C" fn voicevox_error_result_to_message( result_code: VoicevoxResultCode, ) -> *const c_char { - voicevox_core::error_result_to_message(result_code).as_ptr() as *const c_char + BUFFER_MANAGER.memorize_static_str( + voicevox_core::error_result_to_message(result_code).as_ptr() as *const c_char + ) } #[cfg(test)]