From 1e9d4998cebe6399877855b5808b694867945ed8 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Tue, 4 Apr 2023 13:54:33 +0200 Subject: [PATCH] thread safety and performance improv --- MareSynchronos/FileCache/FileCacheManager.cs | 7 ++--- MareSynchronos/MareSynchronos.csproj | 2 +- .../PlayerData/Factories/PlayerDataFactory.cs | 26 ++++++++-------- MareSynchronos/Services/DalamudUtilService.cs | 31 +++++++++++++++---- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/MareSynchronos/FileCache/FileCacheManager.cs b/MareSynchronos/FileCache/FileCacheManager.cs index 053da45..34a05bb 100644 --- a/MareSynchronos/FileCache/FileCacheManager.cs +++ b/MareSynchronos/FileCache/FileCacheManager.cs @@ -105,11 +105,10 @@ public sealed class FileCacheManager : IDisposable public FileCacheEntity? GetFileCacheByHash(string hash) { - if (_fileCaches.Any(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal))) + var entry = _fileCaches.FirstOrDefault(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)); + if (!EqualityComparer>.Default.Equals(entry, default)) { - return GetValidatedFileCache(_fileCaches.Where(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)) - .OrderByDescending(f => f.Value.PrefixedFilePath.Length) - .FirstOrDefault(f => string.Equals(f.Value.Hash, hash, StringComparison.Ordinal)).Value); + return GetValidatedFileCache(entry.Value); } return null; diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index 90c44bf..71c1807 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -3,7 +3,7 @@ - 0.8.17 + 0.8.18 https://github.com/Penumbra-Sync/client diff --git a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs index 6eb152c..520dc91 100644 --- a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs +++ b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs @@ -320,19 +320,6 @@ public class PlayerDataFactory Stopwatch st = Stopwatch.StartNew(); - // gather up data from ipc - previousData.ManipulationString = _ipcManager.PenumbraGetMetaManipulations(); - previousData.HeelsOffset = _ipcManager.GetHeelsOffset(); - Task getGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(playerRelatedObject.Address); - Task getCustomizeData = _ipcManager.GetCustomizePlusScale(); - Task getPalettePlusData = _ipcManager.PalettePlusBuildPalette(); - previousData.GlamourerString[playerRelatedObject.ObjectKind] = await getGlamourerData.ConfigureAwait(false); - _logger.LogDebug("Glamourer is now: {data}", previousData.GlamourerString[playerRelatedObject.ObjectKind]); - previousData.CustomizePlusScale = await getCustomizeData.ConfigureAwait(false); - _logger.LogDebug("Customize is now: {data}", previousData.CustomizePlusScale); - previousData.PalettePlusPalette = await getPalettePlusData.ConfigureAwait(false); - _logger.LogDebug("Palette is now: {data}", previousData.PalettePlusPalette); - // gather static replacements from render model var (forwardResolve, reverseResolve) = BuildDataFromModel(objectKind, charaPointer, token); Dictionary> resolvedPaths = await GetFileReplacementsFromPaths(forwardResolve, reverseResolve).ConfigureAwait(false); @@ -381,6 +368,19 @@ public class PlayerDataFactory previousData.FileReplacements[item.Key] = new HashSet(item.Value.Where(v => v.HasFileReplacement).OrderBy(v => v.ResolvedPath, StringComparer.Ordinal), FileReplacementComparer.Instance); } + // gather up data from ipc + previousData.ManipulationString = _ipcManager.PenumbraGetMetaManipulations(); + previousData.HeelsOffset = _ipcManager.GetHeelsOffset(); + Task getGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(playerRelatedObject.Address); + Task getCustomizeData = _ipcManager.GetCustomizePlusScale(); + Task getPalettePlusData = _ipcManager.PalettePlusBuildPalette(); + previousData.GlamourerString[playerRelatedObject.ObjectKind] = await getGlamourerData.ConfigureAwait(false); + _logger.LogDebug("Glamourer is now: {data}", previousData.GlamourerString[playerRelatedObject.ObjectKind]); + previousData.CustomizePlusScale = await getCustomizeData.ConfigureAwait(false); + _logger.LogDebug("Customize is now: {data}", previousData.CustomizePlusScale); + previousData.PalettePlusPalette = await getPalettePlusData.ConfigureAwait(false); + _logger.LogDebug("Palette is now: {data}", previousData.PalettePlusPalette); + st.Stop(); _logger.LogInformation("Building character data for {obj} took {time}ms", objectKind, TimeSpan.FromTicks(st.ElapsedTicks).TotalMilliseconds); diff --git a/MareSynchronos/Services/DalamudUtilService.cs b/MareSynchronos/Services/DalamudUtilService.cs index 32251ac..19f0bbc 100644 --- a/MareSynchronos/Services/DalamudUtilService.cs +++ b/MareSynchronos/Services/DalamudUtilService.cs @@ -12,6 +12,7 @@ using MareSynchronos.Utils; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System.Numerics; +using System.Runtime.CompilerServices; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; namespace MareSynchronos.Services; @@ -53,6 +54,7 @@ public class DalamudUtilService : IHostedService public unsafe GameObject* GposeTarget => TargetSystem.Instance()->GPoseTarget; public unsafe Dalamud.Game.ClientState.Objects.Types.GameObject? GposeTargetGameObject => GposeTarget == null ? null : _objectTable[GposeTarget->ObjectIndex]; public bool IsInCutscene { get; private set; } = false; + public bool IsInFrameworkThread => _framework.IsInFrameworkUpdateThread; public bool IsInGpose { get; private set; } = false; public bool IsLoggedIn { get; private set; } public bool IsPlayerPresent => _clientState.LocalPlayer != null && _clientState.LocalPlayer.IsValid(); @@ -146,20 +148,37 @@ public class DalamudUtilService : IHostedService return false; } - public async Task RunOnFrameworkThread(Action act) + public async Task RunOnFrameworkThread(Action act, [CallerMemberName] string callerMember = "", + [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int lineNumber = 0) { - _logger.LogTrace("Running Action on framework thread (FrameworkContext: {ctx}): {act}", _framework.IsInFrameworkUpdateThread, act); + _logger.LogTrace("Running Action on framework thread (FrameworkContext: {ctx}): {member} in {file}:{line}", _framework.IsInFrameworkUpdateThread, callerMember, callerFilePath, lineNumber); if (!_framework.IsInFrameworkUpdateThread) - await _framework.RunOnFrameworkThread(act).ConfigureAwait(false); + { + await _framework.RunOnFrameworkThread(act).ContinueWith((_) => Task.CompletedTask).ConfigureAwait(false); + while (_framework.IsInFrameworkUpdateThread) // yield the thread again, should technically never be triggered + { + _logger.LogTrace("Still on framework"); + await Task.Delay(1).ConfigureAwait(false); + } + } else act(); } - public async Task RunOnFrameworkThread(Func func) + public async Task RunOnFrameworkThread(Func func, [CallerMemberName] string callerMember = "", + [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int lineNumber = 0) { - _logger.LogTrace("Running Func on framework thread (FrameworkContext: {ctx}): {act}", _framework.IsInFrameworkUpdateThread, func); + _logger.LogTrace("Running Func on framework thread (FrameworkContext: {ctx}): {member} in {file}:{line}", _framework.IsInFrameworkUpdateThread, callerMember, callerFilePath, lineNumber); if (!_framework.IsInFrameworkUpdateThread) - return await _framework.RunOnFrameworkThread(func).ConfigureAwait(false); + { + var result = await _framework.RunOnFrameworkThread(func).ContinueWith((task) => task.Result).ConfigureAwait(false); + while (_framework.IsInFrameworkUpdateThread) // yield the thread again, should technically never be triggered + { + _logger.LogTrace("Still on framework"); + await Task.Delay(1).ConfigureAwait(false); + } + return result; + } else return func.Invoke(); }