Skip to content

Commit

Permalink
fix(backend): fix false multithread mode
Browse files Browse the repository at this point in the history
Since generics cannot resolve tokio::spawn('static), a destructive change was made to change it to a String and give it ownership.
  • Loading branch information
SARDONYX-sard committed Jan 7, 2024
1 parent ad640b7 commit 3417ddb
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 61 deletions.
4 changes: 2 additions & 2 deletions dar2oar_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ async fn main() -> anyhow::Result<()> {
let config = ConvertOptions {
dar_dir: args.src,
oar_dir: args.dist,
mod_name: args.name.as_deref(),
author: args.author.as_deref(),
mod_name: args.name,
author: args.author,
section_table: get_mapping_table(args.mapping_file).await,
section_1person_table: get_mapping_table(args.mapping_1person_file).await,
run_parallel: args.run_parallel,
Expand Down
4 changes: 2 additions & 2 deletions dar2oar_core/benches/convert_n_thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn criterion_benchmark(c: &mut Criterion) {

parallel::convert_dar_to_oar(
black_box(ConvertOptions {
dar_dir: TARGET,
dar_dir: TARGET.into(),
section_table: Some(mapping),
..Default::default()
}),
Expand All @@ -45,7 +45,7 @@ fn criterion_benchmark(c: &mut Criterion) {

sequential::convert_dar_to_oar(
black_box(ConvertOptions {
dar_dir: TARGET,
dar_dir: TARGET.into(),
section_table: Some(mapping),
..Default::default()
}),
Expand Down
5 changes: 5 additions & 0 deletions dar2oar_core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ pub enum ConvertError {
NotFoundOarDir,
#[error("Not found \"DynamicAnimationReplacer\" directory")]
NotFoundDarDir,
#[error("Never converted.")]
NeverConverted,
#[error("Not found file name")]
NotFoundFileName,
#[error("This is not valid utf8")]
Expand All @@ -32,6 +34,9 @@ pub enum ConvertError {
/// Represents all other cases of `std::io::Error`.
#[error(transparent)]
IOError(#[from] std::io::Error),
/// Thread join error.
#[error(transparent)]
JoinError(#[from] tokio::task::JoinError),
}

//? Implemented to facilitate testing with the `assert_eq!` macro.
Expand Down
29 changes: 16 additions & 13 deletions dar2oar_core/src/fs/converter/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ use crate::fs::converter::{ConvertOptions, ConvertedReport};
use crate::fs::path_changer::ParsedPath;
use crate::fs::section_writer::{read_file, write_name_space_config, write_section_config};
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use tokio::fs;

/// Common parts of multi-threaded and single-threaded loop processing.
/// # Performance
/// - Since dir is created when a file is discovered, performance is improved if path.is_dir() is not put in path.
pub async fn convert_inner(
options: &ConvertOptions<'_, impl AsRef<Path>>,
options: &ConvertOptions,
path: &Path,
parsed_path: ParsedPath,
is_converted_once: &mut bool,
parsed_path: &ParsedPath,
is_converted_once: &AtomicBool,
) -> Result<()> {
let ConvertOptions {
dar_dir: _,
Expand All @@ -29,20 +30,19 @@ pub async fn convert_inner(

let is_1st_person = parsed_path.is_1st_person;
let parsed_mod_name = mod_name
.map(|s| s.to_string())
.clone()
.unwrap_or(parsed_path.mod_name.clone().unwrap_or("Unknown".into()));
let oar_name_space_path = oar_dir
.as_ref()
.map(|path| match is_1st_person {
true => path
.as_ref()
true => Path::new(path)
.join("meshes/actors/character/_1stperson/animations/OpenAnimationReplacer"),
false => path
.as_ref()
.join("meshes/actors/character/animations/OpenAnimationReplacer"),
false => {
Path::new(path).join("meshes/actors/character/animations/OpenAnimationReplacer")
}
})
.unwrap_or(parsed_path.oar_root.clone())
.join(mod_name.unwrap_or(&parsed_mod_name));
.join(&parsed_mod_name);

if path.extension().is_some() {
tracing::debug!("File: {:?}", path);
Expand Down Expand Up @@ -79,9 +79,12 @@ pub async fn convert_inner(
};
write_section_config(section_root, config_json).await?;

if !*is_converted_once {
write_name_space_config(&oar_name_space_path, &parsed_mod_name, *author).await?;
*is_converted_once = true;
if is_converted_once
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed)
.is_ok()
{
write_name_space_config(&oar_name_space_path, &parsed_mod_name, author.as_deref())
.await?;
}
} else {
// maybe motion files(.hkx), gender dir
Expand Down
19 changes: 9 additions & 10 deletions dar2oar_core/src/fs/converter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ pub mod support_cmd;

use crate::error::Result;
use std::collections::HashMap;
use std::path::Path;

/// # Convert DAR to OAR
///
Expand Down Expand Up @@ -75,7 +74,7 @@ use std::path::Path;
/// }
/// ```
pub async fn convert_dar_to_oar(
options: ConvertOptions<'_, impl AsRef<Path>>,
options: ConvertOptions,
progress_fn: impl FnMut(usize),
) -> Result<ConvertedReport> {
match options.run_parallel {
Expand All @@ -90,15 +89,15 @@ impl Closure {
}

#[derive(Debug, Clone, Default)]
pub struct ConvertOptions<'a, P: AsRef<Path>> {
pub struct ConvertOptions {
/// DAR source dir path
pub dar_dir: P,
pub dar_dir: String,
/// OAR destination dir path(If not, it is inferred from src)
pub oar_dir: Option<P>,
pub oar_dir: Option<String>,
/// mod name in config.json & directory name(If not, it is inferred from src)
pub mod_name: Option<&'a str>,
pub mod_name: Option<String>,
/// mod author in config.json
pub author: Option<&'a str>,
pub author: Option<String>,
/// path to section name table
pub section_table: Option<HashMap<String, String>>,
/// path to section name table(For _1st_person)
Expand Down Expand Up @@ -151,12 +150,12 @@ mod test {
};
}

async fn create_options<'a>() -> Result<ConvertOptions<'a, &'a str>> {
async fn create_options() -> Result<ConvertOptions> {
Ok(ConvertOptions {
dar_dir: DAR_DIR,
dar_dir: DAR_DIR.into(),
// cannot use include_str!
section_table: Some(crate::read_mapping_table(TABLE_PATH).await?),
// run_parallel: true,
run_parallel: true,
..Default::default()
})
}
Expand Down
48 changes: 37 additions & 11 deletions dar2oar_core/src/fs/converter/parallel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use crate::fs::converter::{ConvertOptions, ConvertedReport};
use crate::fs::path_changer::parse_dar_path;
use jwalk::WalkDirGeneric;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

/// Multi thread converter
///
Expand All @@ -18,43 +20,67 @@ use std::path::Path;
/// For library reasons, you get the number of DAR dirs and files, not the number of DAR files only
/// (i.e., the count is different from the Sequential version)
pub async fn convert_dar_to_oar(
options: ConvertOptions<'_, impl AsRef<Path>>,
options: ConvertOptions,
mut progress_fn: impl FnMut(usize),
) -> Result<ConvertedReport> {
let dar_dir = options.dar_dir.as_ref();
let dar_dir = options.dar_dir.as_str();

let walk_len = get_dar_files(dar_dir).into_iter().count();
tracing::debug!("Parallel Converter/DAR dir & file counts: {}", walk_len);
progress_fn(walk_len);

let entires = get_dar_files(dar_dir).into_iter();
let hide_dar = options.hide_dar;
let options = Arc::new(options);

let mut dar_1st_namespace = None; // To need rename to hidden(For _1stperson)
let mut dar_namespace = None; // To need rename to hidden
let mut is_converted_once = false;
let is_converted_once = Arc::new(AtomicBool::new(false));
let mut task_handles: Vec<tokio::task::JoinHandle<Result<()>>> = Vec::new();

for (idx, entry) in entires.enumerate() {
let path = entry.map_err(|_| ConvertError::NotFoundEntry)?.path();
let path = path.as_path();
let parsed_path = match parse_dar_path(path, None) {
let parsed_path = Arc::new(match parse_dar_path(&path, None) {
Ok(p) => p,
Err(_) => continue,
};
tracing::debug!("[Start {}th conversion]\n{:?}", idx, &parsed_path);
});
let path = Arc::new(path);

if dar_1st_namespace.is_none() && parsed_path.is_1st_person {
dar_1st_namespace = Some(parsed_path.dar_root.clone());
} else if dar_namespace.is_none() {
dar_namespace = Some(parsed_path.dar_root.clone());
}
convert_inner(&options, path, parsed_path, &mut is_converted_once).await?;

task_handles.push(tokio::spawn({
let path = Arc::clone(&path);
let parsed_path = Arc::clone(&parsed_path);
let options = Arc::clone(&options);
let is_converted_once = Arc::clone(&is_converted_once);
async move {
tracing::debug!("[Start {}th conversion]\n{:?}", idx, &parsed_path);
convert_inner(
&options,
&path,
parsed_path.as_ref(),
is_converted_once.as_ref(),
)
.await?;
tracing::debug!("[End {}th conversion]\n\n", idx);
Ok(())
}
}));
}

for (idx, task_handle) in task_handles.into_iter().enumerate() {
task_handle.await??;
progress_fn(idx);
tracing::debug!("[End {}th conversion]\n\n", idx);
}

if is_converted_once {
if is_converted_once.load(Ordering::Acquire) {
handle_conversion_results(hide_dar, &dar_namespace, &dar_1st_namespace).await
} else {
Err(ConvertError::NotFoundDarDir)
Err(ConvertError::NeverConverted)
}
}

Expand Down
13 changes: 7 additions & 6 deletions dar2oar_core/src/fs/converter/sequential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::fs::converter::{ConvertOptions, ConvertedReport};
use crate::fs::path_changer::parse_dar_path;
use async_walkdir::{Filtering, WalkDir};
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use tokio_stream::StreamExt;

/// Single thread converter
Expand All @@ -15,10 +16,10 @@ use tokio_stream::StreamExt;
/// # Return
/// Complete info
pub async fn convert_dar_to_oar(
options: ConvertOptions<'_, impl AsRef<Path>>,
options: ConvertOptions,
mut progress_fn: impl FnMut(usize),
) -> Result<ConvertedReport> {
let dar_dir = options.dar_dir.as_ref();
let dar_dir = options.dar_dir.as_str();

let walk_len = get_dar_file_count(dar_dir).await?;
tracing::debug!("Sequential Converter/DAR file counts: {}", walk_len);
Expand All @@ -28,7 +29,7 @@ pub async fn convert_dar_to_oar(
let mut dar_1st_namespace = None; // To need rename to hidden(For _1stperson)
let mut dar_namespace = None; // To need rename to hidden
let mut entries = get_dar_files(dar_dir).await;
let mut is_converted_once = false;
let is_converted_once = AtomicBool::new(false);

let mut idx = 0usize;
while let Some(entry) = entries.next().await {
Expand All @@ -48,17 +49,17 @@ pub async fn convert_dar_to_oar(
} else if dar_namespace.is_none() {
dar_namespace = Some(parsed_path.dar_root.clone());
}
convert_inner(&options, path, parsed_path, &mut is_converted_once).await?;
convert_inner(&options, path, &parsed_path, &is_converted_once).await?;

progress_fn(idx);
tracing::debug!("[End {}th conversion]\n\n", idx);
idx += 1;
}

if is_converted_once {
if is_converted_once.load(Ordering::Acquire) {
handle_conversion_results(hide_dar, &dar_namespace, &dar_1st_namespace).await
} else {
Err(ConvertError::NotFoundDarDir)
Err(ConvertError::NeverConverted)
}
}

Expand Down
6 changes: 3 additions & 3 deletions locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
"custom-js-label": "Custom JavaScript(Please do not execute untrusted scripts)",
"editor-mode-list-label": "Editor Mode",
"hide-dar-btn": "Hide DAR",
"hide-dar-btn-tooltip": "After conversion, append \".mohidden\" to the DAR dirname in \"DAR(src) Directory*\" to make it a hidden directory(For MO2 users)",
"hide-dar-btn-tooltip2": "NOTE: Failure to cross the drive or No permission.",
"hide-dar-btn-tooltip": "After conversion, append \".mohidden\" to the DAR dirname in \"DAR(src) Directory*\" to make it a hidden directory.",
"hide-dar-btn-tooltip2": "NOTE: It appears to work on the MO2 Tree view, but it is doubtful that it works in the author's actual experience.",
"log-level-list-label": "Log Level",
"mapping-wiki-url-leaf": "wiki#what-is-the-mapping-file",
"open-log-btn": "Open log",
Expand All @@ -43,7 +43,7 @@
"remove-oar-success": "Removed OAR directory.",
"remove-oar-tooltip": "Find and delete OAR dir from \"DAR(src) Directory*\" or \"OAR(dist) Directory\".",
"run-parallel-btn-tooltip": "Use multi-threading.",
"run-parallel-btn-tooltip2": "In most cases, it slows down by tens of ms, but may be effective when there is more weight on CPU processing with fewer files to copy and more logic parsing of \"_condition.txt\"",
"run-parallel-btn-tooltip2": "More than twice the processing speed can be expected, but the logs are difficult to read and may fail due to unexpected bugs.",
"run-parallel-label": "Run Parallel",
"select-btn": "Select",
"unhide-dar-btn": "Unhide DAR",
Expand Down
6 changes: 3 additions & 3 deletions locales/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
"custom-js-label": "カスタムJavaScript(信用できないスクリプトは実行しないでください)",
"editor-mode-list-label": "編集モード",
"hide-dar-btn": "DARを非表示",
"hide-dar-btn-tooltip": "変換後に\"DAR(src) Directory*\"\".mohidden\"を追加して隠しディレクトリ化(MO2ユーザ向け)",
"hide-dar-btn-tooltip2": "注意: DARの保存先の書き込み権限がない場合、エラーが発生する可能性があります",
"hide-dar-btn-tooltip": "変換後に\"DAR(src) Directory*\"\".mohidden\"を追加して隠しディレクトリ化します",
"hide-dar-btn-tooltip2": "注意: MO2のTree view上では機能しているように見えますが作者の実体験では機能しているか怪しいです。",
"log-level-list-label": "ログレベル",
"mapping-wiki-url-leaf": "wiki#what-is-the-mapping-file",
"open-log-btn": "ログを開く",
Expand All @@ -43,7 +43,7 @@
"remove-oar-success": "OARディレクトリを削除しました",
"remove-oar-tooltip": "OAR(dist)(なければDAR(src))からOARディレクトリを捜索して削除します",
"run-parallel-btn-tooltip": "マルチスレッドを使用します",
"run-parallel-btn-tooltip2": "大抵の場合で数十ミリ秒遅延しますが、コピー対象が少なく、CPU処理の比重が大きい(\"_condition.txt\"の解析が多い)時には有効かもしれません",
"run-parallel-btn-tooltip2": "2倍以上の処理速度が期待できますが、ログが読みづらく思わぬバグで失敗する可能性があります",
"run-parallel-label": "並列実行",
"select-btn": "選択",
"unhide-dar-btn": "DAR再表示",
Expand Down
4 changes: 2 additions & 2 deletions src-tauri/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ macro_rules! bail {
}

#[tauri::command]
pub(crate) async fn convert_dar2oar(options: GuiConverterOptions<'_>) -> Result<String, String> {
pub(crate) async fn convert_dar2oar(options: GuiConverterOptions) -> Result<String, String> {
dar2oar!(options, Closure::default)
}

#[tauri::command]
pub(crate) async fn convert_dar2oar_with_progress(
window: Window,
options: GuiConverterOptions<'_>,
options: GuiConverterOptions,
) -> Result<String, String> {
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct Payload {
Expand Down
18 changes: 9 additions & 9 deletions src-tauri/src/convert_option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct GuiConverterOptions<'a> {
pub(crate) dar_dir: &'a str,
pub(crate) oar_dir: Option<&'a str>,
pub(crate) mod_name: Option<&'a str>,
pub(crate) mod_author: Option<&'a str>,
pub(crate) mapping_path: Option<&'a str>,
pub(crate) mapping_1person_path: Option<&'a str>,
pub(crate) struct GuiConverterOptions {
pub(crate) dar_dir: String,
pub(crate) oar_dir: Option<String>,
pub(crate) mod_name: Option<String>,
pub(crate) mod_author: Option<String>,
pub(crate) mapping_path: Option<String>,
pub(crate) mapping_1person_path: Option<String>,
pub(crate) run_parallel: Option<bool>,
pub(crate) hide_dar: Option<bool>,
}
Expand All @@ -20,8 +20,8 @@ pub(crate) trait AsyncFrom<T> {
}

#[async_trait::async_trait]
impl<'a> AsyncFrom<GuiConverterOptions<'a>> for ConvertOptions<'a, &'a str> {
async fn async_from(options: GuiConverterOptions<'a>) -> Self {
impl AsyncFrom<GuiConverterOptions> for ConvertOptions {
async fn async_from(options: GuiConverterOptions) -> Self {
let GuiConverterOptions {
dar_dir,
oar_dir,
Expand Down

0 comments on commit 3417ddb

Please sign in to comment.