From 72b4afdea688bebc8d2b7eefd72815c8adf491f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Oct 2021 16:51:34 +0900 Subject: [PATCH 1/3] Move `RealmRulesetStore` initial work off incorrect thread --- osu.Game/Stores/RealmRulesetStore.cs | 117 ++++++++++++++------------- 1 file changed, 60 insertions(+), 57 deletions(-) diff --git a/osu.Game/Stores/RealmRulesetStore.cs b/osu.Game/Stores/RealmRulesetStore.cs index e9c04f652d..0d2cddf874 100644 --- a/osu.Game/Stores/RealmRulesetStore.cs +++ b/osu.Game/Stores/RealmRulesetStore.cs @@ -102,75 +102,78 @@ namespace osu.Game.Stores private void addMissingRulesets() { - realmFactory.Context.Write(realm => + using (var context = realmFactory.CreateContext()) { - var rulesets = realm.All(); - - List instances = loadedAssemblies.Values - .Select(r => Activator.CreateInstance(r) as Ruleset) - .Where(r => r != null) - .Select(r => r.AsNonNull()) - .ToList(); - - // add all legacy rulesets first to ensure they have exclusive choice of primary key. - foreach (var r in instances.Where(r => r is ILegacyRuleset)) + context.Write(realm => { - if (realm.All().FirstOrDefault(rr => rr.OnlineID == r.RulesetInfo.ID) == null) - realm.Add(new RealmRuleset(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.ID)); - } + var rulesets = realm.All(); - // add any other rulesets which have assemblies present but are not yet in the database. - foreach (var r in instances.Where(r => !(r is ILegacyRuleset))) - { - if (rulesets.FirstOrDefault(ri => ri.InstantiationInfo.Equals(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null) + List instances = loadedAssemblies.Values + .Select(r => Activator.CreateInstance(r) as Ruleset) + .Where(r => r != null) + .Select(r => r.AsNonNull()) + .ToList(); + + // add all legacy rulesets first to ensure they have exclusive choice of primary key. + foreach (var r in instances.Where(r => r is ILegacyRuleset)) { - var existingSameShortName = rulesets.FirstOrDefault(ri => ri.ShortName == r.RulesetInfo.ShortName); - - if (existingSameShortName != null) - { - // even if a matching InstantiationInfo was not found, there may be an existing ruleset with the same ShortName. - // this generally means the user or ruleset provider has renamed their dll but the underlying ruleset is *likely* the same one. - // in such cases, update the instantiation info of the existing entry to point to the new one. - existingSameShortName.InstantiationInfo = r.RulesetInfo.InstantiationInfo; - } - else + if (realm.All().FirstOrDefault(rr => rr.OnlineID == r.RulesetInfo.ID) == null) realm.Add(new RealmRuleset(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.ID)); } - } - List detachedRulesets = new List(); - - // perform a consistency check and detach final rulesets from realm for cross-thread runtime usage. - foreach (var r in rulesets) - { - try + // add any other rulesets which have assemblies present but are not yet in the database. + foreach (var r in instances.Where(r => !(r is ILegacyRuleset))) { - var type = Type.GetType(r.InstantiationInfo); + if (rulesets.FirstOrDefault(ri => ri.InstantiationInfo.Equals(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null) + { + var existingSameShortName = rulesets.FirstOrDefault(ri => ri.ShortName == r.RulesetInfo.ShortName); - if (type == null) - throw new InvalidOperationException(@"Type resolution failure."); - - var rInstance = (Activator.CreateInstance(type) as Ruleset)?.RulesetInfo; - - if (rInstance == null) - throw new InvalidOperationException(@"Instantiation failure."); - - r.Name = rInstance.Name; - r.ShortName = rInstance.ShortName; - r.InstantiationInfo = rInstance.InstantiationInfo; - r.Available = true; - - detachedRulesets.Add(r.Clone()); + if (existingSameShortName != null) + { + // even if a matching InstantiationInfo was not found, there may be an existing ruleset with the same ShortName. + // this generally means the user or ruleset provider has renamed their dll but the underlying ruleset is *likely* the same one. + // in such cases, update the instantiation info of the existing entry to point to the new one. + existingSameShortName.InstantiationInfo = r.RulesetInfo.InstantiationInfo; + } + else + realm.Add(new RealmRuleset(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.ID)); + } } - catch (Exception ex) + + List detachedRulesets = new List(); + + // perform a consistency check and detach final rulesets from realm for cross-thread runtime usage. + foreach (var r in rulesets) { - r.Available = false; - Logger.Log($"Could not load ruleset {r}: {ex.Message}"); - } - } + try + { + var type = Type.GetType(r.InstantiationInfo); - availableRulesets.AddRange(detachedRulesets); - }); + if (type == null) + throw new InvalidOperationException(@"Type resolution failure."); + + var rInstance = (Activator.CreateInstance(type) as Ruleset)?.RulesetInfo; + + if (rInstance == null) + throw new InvalidOperationException(@"Instantiation failure."); + + r.Name = rInstance.Name; + r.ShortName = rInstance.ShortName; + r.InstantiationInfo = rInstance.InstantiationInfo; + r.Available = true; + + detachedRulesets.Add(r.Clone()); + } + catch (Exception ex) + { + r.Available = false; + Logger.Log($"Could not load ruleset {r}: {ex.Message}"); + } + } + + availableRulesets.AddRange(detachedRulesets); + }); + } } private void loadFromAppDomain() From 413f98999a06f54b7171ad299f401911b4865cf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Oct 2021 16:33:09 +0900 Subject: [PATCH 2/3] Start initialising `RealmRulesetStore` on startup --- osu.Game/OsuGameBase.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f6ec22a536..c1cc2a7f25 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -40,6 +40,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Skinning; +using osu.Game.Stores; using osu.Game.Utils; using RuntimeInfo = osu.Framework.RuntimeInfo; @@ -237,6 +238,11 @@ namespace osu.Game dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Scheduler, Host, () => difficultyCache, LocalConfig)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, performOnlineLookups: true)); + // the following realm components are not actively used yet, but initialised and kept up to date for initial testing. + var realmRulesetStore = new RealmRulesetStore(realmFactory, Storage); + + dependencies.Cache(realmRulesetStore); + // this should likely be moved to ArchiveModelManager when another case appears where it is necessary // to have inter-dependent model managers. this could be obtained with an IHasForeign interface to // allow lookups to be done on the child (ScoreManager in this case) to perform the cascading delete. From ea0715cce4083ea896f58c1d7763a893b903d9d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Nov 2021 16:35:05 +0900 Subject: [PATCH 3/3] Add missing dispose call --- osu.Game/OsuGameBase.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 3f7339c031..7911e843d1 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -161,6 +161,8 @@ namespace osu.Game private readonly BindableNumber globalTrackVolumeAdjust = new BindableNumber(GLOBAL_TRACK_VOLUME_ADJUST); + private RealmRulesetStore realmRulesetStore; + public OsuGameBase() { UseDevelopmentServer = DebugUtils.IsDebugBuild; @@ -233,7 +235,7 @@ namespace osu.Game dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, performOnlineLookups: true)); // the following realm components are not actively used yet, but initialised and kept up to date for initial testing. - var realmRulesetStore = new RealmRulesetStore(realmFactory, Storage); + realmRulesetStore = new RealmRulesetStore(realmFactory, Storage); dependencies.Cache(realmRulesetStore); @@ -518,6 +520,8 @@ namespace osu.Game LocalConfig?.Dispose(); contextFactory?.FlushConnections(); + + realmRulesetStore?.Dispose(); realmFactory?.Dispose(); } }