diff --git a/osu.Game.Benchmarks/BenchmarkRealmReads.cs b/osu.Game.Benchmarks/BenchmarkRealmReads.cs index 5b5bdf595d..bb22fab51c 100644 --- a/osu.Game.Benchmarks/BenchmarkRealmReads.cs +++ b/osu.Game.Benchmarks/BenchmarkRealmReads.cs @@ -29,8 +29,10 @@ namespace osu.Game.Benchmarks realmFactory = new RealmContextFactory(storage, "client"); - using (var context = realmFactory.CreateContext()) - context.Write(c => c.Add(TestResources.CreateTestBeatmapSetInfo(rulesets: new[] { new OsuRuleset().RulesetInfo }))); + realmFactory.Run(realm => + { + realm.Write(c => c.Add(TestResources.CreateTestBeatmapSetInfo(rulesets: new[] { new OsuRuleset().RulesetInfo }))); + }); updateThread = new UpdateThread(() => { }, null); updateThread.Start(); @@ -39,15 +41,15 @@ namespace osu.Game.Benchmarks [Benchmark] public void BenchmarkDirectPropertyRead() { - using (var context = realmFactory.CreateContext()) + realmFactory.Run(realm => { - var beatmapSet = context.All().First(); + var beatmapSet = realm.All().First(); for (int i = 0; i < ReadsPerFetch; i++) { string _ = beatmapSet.Beatmaps.First().Hash; } - } + }); } [Benchmark] @@ -78,15 +80,15 @@ namespace osu.Game.Benchmarks [Benchmark] public void BenchmarkRealmLivePropertyRead() { - using (var context = realmFactory.CreateContext()) + realmFactory.Run(realm => { - var beatmapSet = context.All().First().ToLive(realmFactory); + var beatmapSet = realm.All().First().ToLive(realmFactory); for (int i = 0; i < ReadsPerFetch; i++) { string _ = beatmapSet.PerformRead(b => b.Beatmaps.First().Hash); } - } + }); } [Benchmark] @@ -117,15 +119,15 @@ namespace osu.Game.Benchmarks [Benchmark] public void BenchmarkDetachedPropertyRead() { - using (var context = realmFactory.CreateContext()) + realmFactory.Run(realm => { - var beatmapSet = context.All().First().Detach(); + var beatmapSet = realm.All().First().Detach(); for (int i = 0; i < ReadsPerFetch; i++) { string _ = beatmapSet.Beatmaps.First().Hash; } - } + }); } [GlobalCleanup] diff --git a/osu.Game.Tests/Beatmaps/IO/BeatmapImportHelper.cs b/osu.Game.Tests/Beatmaps/IO/BeatmapImportHelper.cs index 44f6943871..7aa2dc7093 100644 --- a/osu.Game.Tests/Beatmaps/IO/BeatmapImportHelper.cs +++ b/osu.Game.Tests/Beatmaps/IO/BeatmapImportHelper.cs @@ -55,8 +55,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var realmContextFactory = osu.Dependencies.Get(); - using (var realm = realmContextFactory.CreateContext()) - BeatmapImporterTests.EnsureLoaded(realm, timeout); + realmContextFactory.Run(realm => BeatmapImporterTests.EnsureLoaded(realm, timeout)); // TODO: add back some extra checks outside of the realm ones? // var set = queryBeatmapSets().First(); diff --git a/osu.Game.Tests/Database/GeneralUsageTests.cs b/osu.Game.Tests/Database/GeneralUsageTests.cs index 0961ad71e4..9ebe94b383 100644 --- a/osu.Game.Tests/Database/GeneralUsageTests.cs +++ b/osu.Game.Tests/Database/GeneralUsageTests.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Database [Test] public void TestConstructRealm() { - RunTestWithRealm((realmFactory, _) => { realmFactory.CreateContext().Refresh(); }); + RunTestWithRealm((realmFactory, _) => { realmFactory.Run(realm => realm.Refresh()); }); } [Test] @@ -46,23 +46,21 @@ namespace osu.Game.Tests.Database { bool callbackRan = false; - using (var context = realmFactory.CreateContext()) + realmFactory.Run(realm => { - var subscription = context.All().QueryAsyncWithNotifications((sender, changes, error) => + var subscription = realm.All().QueryAsyncWithNotifications((sender, changes, error) => { - using (realmFactory.CreateContext()) + realmFactory.Run(_ => { callbackRan = true; - } + }); }); // Force the callback above to run. - using (realmFactory.CreateContext()) - { - } + realmFactory.Run(r => r.Refresh()); subscription?.Dispose(); - } + }); Assert.IsTrue(callbackRan); }); @@ -78,12 +76,12 @@ namespace osu.Game.Tests.Database Task.Factory.StartNew(() => { - using (realmFactory.CreateContext()) + realmFactory.Run(_ => { hasThreadedUsage.Set(); stopThreadedUsage.Wait(); - } + }); }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler); hasThreadedUsage.Wait(); diff --git a/osu.Game.Tests/Database/RealmLiveTests.cs b/osu.Game.Tests/Database/RealmLiveTests.cs index 187fcd3ca7..7b1cf763d6 100644 --- a/osu.Game.Tests/Database/RealmLiveTests.cs +++ b/osu.Game.Tests/Database/RealmLiveTests.cs @@ -23,9 +23,9 @@ namespace osu.Game.Tests.Database { RunTestWithRealm((realmFactory, _) => { - ILive beatmap = realmFactory.CreateContext().Write(r => r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()))).ToLive(realmFactory); + ILive beatmap = realmFactory.Run(realm => realm.Write(r => r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()))).ToLive(realmFactory)); - ILive beatmap2 = realmFactory.CreateContext().All().First().ToLive(realmFactory); + ILive beatmap2 = realmFactory.Run(realm => realm.All().First().ToLive(realmFactory)); Assert.AreEqual(beatmap, beatmap2); }); @@ -38,13 +38,18 @@ namespace osu.Game.Tests.Database { var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()); - ILive liveBeatmap; + ILive? liveBeatmap = null; - using (var context = realmFactory.CreateContext()) + realmFactory.Run(realm => { - context.Write(r => r.Add(beatmap)); + realm.Write(r => r.Add(beatmap)); liveBeatmap = beatmap.ToLive(realmFactory); + }); + + using (realmFactory.BlockAllOperations()) + { + // recycle realm before migrating } using (var migratedStorage = new TemporaryNativeStorage("realm-test-migration-target")) @@ -53,7 +58,7 @@ namespace osu.Game.Tests.Database storage.Migrate(migratedStorage); - Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden)); + Assert.IsFalse(liveBeatmap?.PerformRead(l => l.Hidden)); } }); } @@ -67,8 +72,7 @@ namespace osu.Game.Tests.Database var liveBeatmap = beatmap.ToLive(realmFactory); - using (var context = realmFactory.CreateContext()) - context.Write(r => r.Add(beatmap)); + realmFactory.Run(realm => realm.Write(r => r.Add(beatmap))); Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden)); }); @@ -99,12 +103,12 @@ namespace osu.Game.Tests.Database ILive? liveBeatmap = null; Task.Factory.StartNew(() => { - using (var threadContext = realmFactory.CreateContext()) + realmFactory.Run(threadContext => { var beatmap = threadContext.Write(r => r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()))); liveBeatmap = beatmap.ToLive(realmFactory); - } + }); }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); Debug.Assert(liveBeatmap != null); @@ -128,12 +132,12 @@ namespace osu.Game.Tests.Database ILive? liveBeatmap = null; Task.Factory.StartNew(() => { - using (var threadContext = realmFactory.CreateContext()) + realmFactory.Run(threadContext => { var beatmap = threadContext.Write(r => r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()))); liveBeatmap = beatmap.ToLive(realmFactory); - } + }); }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); Debug.Assert(liveBeatmap != null); @@ -170,12 +174,12 @@ namespace osu.Game.Tests.Database Task.Factory.StartNew(() => { - using (var threadContext = realmFactory.CreateContext()) + realmFactory.Run(threadContext => { var beatmap = threadContext.Write(r => r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()))); liveBeatmap = beatmap.ToLive(realmFactory); - } + }); }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); Debug.Assert(liveBeatmap != null); @@ -189,13 +193,13 @@ namespace osu.Game.Tests.Database }); // Can't be used, even from within a valid context. - using (realmFactory.CreateContext()) + realmFactory.Run(threadContext => { Assert.Throws(() => { var __ = liveBeatmap.Value; }); - } + }); }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); }); } @@ -208,12 +212,12 @@ namespace osu.Game.Tests.Database ILive? liveBeatmap = null; Task.Factory.StartNew(() => { - using (var threadContext = realmFactory.CreateContext()) + realmFactory.Run(threadContext => { var beatmap = threadContext.Write(r => r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()))); liveBeatmap = beatmap.ToLive(realmFactory); - } + }); }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); Debug.Assert(liveBeatmap != null); @@ -235,50 +239,50 @@ namespace osu.Game.Tests.Database { int changesTriggered = 0; - using (var updateThreadContext = realmFactory.CreateContext()) + realmFactory.Run(outerRealm => { - updateThreadContext.All().QueryAsyncWithNotifications(gotChange); + outerRealm.All().QueryAsyncWithNotifications(gotChange); ILive? liveBeatmap = null; Task.Factory.StartNew(() => { - using (var threadContext = realmFactory.CreateContext()) + realmFactory.Run(innerRealm => { var ruleset = CreateRuleset(); - var beatmap = threadContext.Write(r => r.Add(new BeatmapInfo(ruleset, new BeatmapDifficulty(), new BeatmapMetadata()))); + var beatmap = innerRealm.Write(r => r.Add(new BeatmapInfo(ruleset, new BeatmapDifficulty(), new BeatmapMetadata()))); // add a second beatmap to ensure that a full refresh occurs below. // not just a refresh from the resolved Live. - threadContext.Write(r => r.Add(new BeatmapInfo(ruleset, new BeatmapDifficulty(), new BeatmapMetadata()))); + innerRealm.Write(r => r.Add(new BeatmapInfo(ruleset, new BeatmapDifficulty(), new BeatmapMetadata()))); liveBeatmap = beatmap.ToLive(realmFactory); - } + }); }, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely(); Debug.Assert(liveBeatmap != null); // not yet seen by main context - Assert.AreEqual(0, updateThreadContext.All().Count()); + Assert.AreEqual(0, outerRealm.All().Count()); Assert.AreEqual(0, changesTriggered); liveBeatmap.PerformRead(resolved => { // retrieval causes an implicit refresh. even changes that aren't related to the retrieval are fired at this point. // ReSharper disable once AccessToDisposedClosure - Assert.AreEqual(2, updateThreadContext.All().Count()); + Assert.AreEqual(2, outerRealm.All().Count()); Assert.AreEqual(1, changesTriggered); // can access properties without a crash. Assert.IsFalse(resolved.Hidden); // ReSharper disable once AccessToDisposedClosure - updateThreadContext.Write(r => + outerRealm.Write(r => { // can use with the main context. r.Remove(resolved); }); }); - } + }); void gotChange(IRealmCollection sender, ChangeSet changes, Exception error) { diff --git a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs index e3c1d42667..c1041e9fd6 100644 --- a/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs +++ b/osu.Game.Tests/Database/TestRealmKeyBindingStore.cs @@ -60,15 +60,12 @@ namespace osu.Game.Tests.Database KeyBindingContainer testContainer = new TestKeyBindingContainer(); // Add some excess bindings for an action which only supports 1. - using (var realm = realmContextFactory.CreateContext()) - using (var transaction = realm.BeginWrite()) + realmContextFactory.Write(realm => { realm.Add(new RealmKeyBinding(GlobalAction.Back, new KeyCombination(InputKey.A))); realm.Add(new RealmKeyBinding(GlobalAction.Back, new KeyCombination(InputKey.S))); realm.Add(new RealmKeyBinding(GlobalAction.Back, new KeyCombination(InputKey.D))); - - transaction.Commit(); - } + }); Assert.That(queryCount(GlobalAction.Back), Is.EqualTo(3)); @@ -79,13 +76,13 @@ namespace osu.Game.Tests.Database private int queryCount(GlobalAction? match = null) { - using (var realm = realmContextFactory.CreateContext()) + return realmContextFactory.Run(realm => { var results = realm.All(); if (match.HasValue) results = results.Where(k => k.ActionInt == (int)match.Value); return results.Count(); - } + }); } [Test] @@ -95,26 +92,26 @@ namespace osu.Game.Tests.Database keyBindingStore.Register(testContainer, Enumerable.Empty()); - using (var primaryRealm = realmContextFactory.CreateContext()) + realmContextFactory.Run(outerRealm => { - var backBinding = primaryRealm.All().Single(k => k.ActionInt == (int)GlobalAction.Back); + var backBinding = outerRealm.All().Single(k => k.ActionInt == (int)GlobalAction.Back); Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.Escape })); var tsr = ThreadSafeReference.Create(backBinding); - using (var threadedContext = realmContextFactory.CreateContext()) + realmContextFactory.Run(innerRealm => { - var binding = threadedContext.ResolveReference(tsr); - threadedContext.Write(() => binding.KeyCombination = new KeyCombination(InputKey.BackSpace)); - } + var binding = innerRealm.ResolveReference(tsr); + innerRealm.Write(() => binding.KeyCombination = new KeyCombination(InputKey.BackSpace)); + }); Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace })); // check still correct after re-query. - backBinding = primaryRealm.All().Single(k => k.ActionInt == (int)GlobalAction.Back); + backBinding = outerRealm.All().Single(k => k.ActionInt == (int)GlobalAction.Back); Assert.That(backBinding.KeyCombination.Keys, Is.EquivalentTo(new[] { InputKey.BackSpace })); - } + }); } [TearDown] diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 8c24b2eef8..1d639c6418 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -60,8 +60,8 @@ namespace osu.Game.Tests.Online testBeatmapInfo = getTestBeatmapInfo(testBeatmapFile); testBeatmapSet = testBeatmapInfo.BeatmapSet; - ContextFactory.Context.Write(r => r.RemoveAll()); - ContextFactory.Context.Write(r => r.RemoveAll()); + ContextFactory.Write(r => r.RemoveAll()); + ContextFactory.Write(r => r.RemoveAll()); selectedItem.Value = new PlaylistItem { diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs index 62500babc1..a77480ee54 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneResultsScreen.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Ranking { base.LoadComplete(); - using (var realm = realmContextFactory.CreateContext()) + realmContextFactory.Run(realm => { var beatmapInfo = realm.All() .Filter($"{nameof(BeatmapInfo.Ruleset)}.{nameof(RulesetInfo.OnlineID)} = $0", 0) @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Ranking if (beatmapInfo != null) Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo); - } + }); } [Test] diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 1e14e4b3e5..f43354514b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -122,11 +122,11 @@ namespace osu.Game.Tests.Visual.UserInterface [SetUp] public void Setup() => Schedule(() => { - using (var realm = realmFactory.CreateContext()) + realmFactory.Run(realm => { // Due to soft deletions, we can re-use deleted scores between test runs scoreManager.Undelete(realm.All().Where(s => s.DeletePending).ToList()); - } + }); leaderboard.Scores = null; leaderboard.FinishTransforms(true); // After setting scores, we may be waiting for transforms to expire drawables diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index ee649ad960..43e4b482bd 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -119,15 +119,17 @@ namespace osu.Game.Beatmaps /// The beatmap difficulty to hide. public void Hide(BeatmapInfo beatmapInfo) { - using (var realm = contextFactory.CreateContext()) - using (var transaction = realm.BeginWrite()) + contextFactory.Run(realm => { - if (!beatmapInfo.IsManaged) - beatmapInfo = realm.Find(beatmapInfo.ID); + using (var transaction = realm.BeginWrite()) + { + if (!beatmapInfo.IsManaged) + beatmapInfo = realm.Find(beatmapInfo.ID); - beatmapInfo.Hidden = true; - transaction.Commit(); - } + beatmapInfo.Hidden = true; + transaction.Commit(); + } + }); } /// @@ -136,27 +138,31 @@ namespace osu.Game.Beatmaps /// The beatmap difficulty to restore. public void Restore(BeatmapInfo beatmapInfo) { - using (var realm = contextFactory.CreateContext()) - using (var transaction = realm.BeginWrite()) + contextFactory.Run(realm => { - if (!beatmapInfo.IsManaged) - beatmapInfo = realm.Find(beatmapInfo.ID); + using (var transaction = realm.BeginWrite()) + { + if (!beatmapInfo.IsManaged) + beatmapInfo = realm.Find(beatmapInfo.ID); - beatmapInfo.Hidden = false; - transaction.Commit(); - } + beatmapInfo.Hidden = false; + transaction.Commit(); + } + }); } public void RestoreAll() { - using (var realm = contextFactory.CreateContext()) - using (var transaction = realm.BeginWrite()) + contextFactory.Run(realm => { - foreach (var beatmap in realm.All().Where(b => b.Hidden)) - beatmap.Hidden = false; + using (var transaction = realm.BeginWrite()) + { + foreach (var beatmap in realm.All().Where(b => b.Hidden)) + beatmap.Hidden = false; - transaction.Commit(); - } + transaction.Commit(); + } + }); } /// @@ -165,8 +171,11 @@ namespace osu.Game.Beatmaps /// A list of available . public List GetAllUsableBeatmapSets() { - using (var context = contextFactory.CreateContext()) - return context.All().Where(b => !b.DeletePending).Detach(); + return contextFactory.Run(realm => + { + realm.Refresh(); + return realm.All().Where(b => !b.DeletePending).Detach(); + }); } /// @@ -176,8 +185,7 @@ namespace osu.Game.Beatmaps /// The first result for the provided query, or null if no results were found. public ILive? QueryBeatmapSet(Expression> query) { - using (var context = contextFactory.CreateContext()) - return context.All().FirstOrDefault(query)?.ToLive(contextFactory); + return contextFactory.Run(realm => realm.All().FirstOrDefault(query)?.ToLive(contextFactory)); } #region Delegation to BeatmapModelManager (methods which previously existed locally). @@ -232,21 +240,20 @@ namespace osu.Game.Beatmaps public void Delete(Expression>? filter = null, bool silent = false) { - using (var context = contextFactory.CreateContext()) + contextFactory.Run(realm => { - var items = context.All().Where(s => !s.DeletePending && !s.Protected); + var items = realm.All().Where(s => !s.DeletePending && !s.Protected); if (filter != null) items = items.Where(filter); beatmapModelManager.Delete(items.ToList(), silent); - } + }); } public void UndeleteAll() { - using (var context = contextFactory.CreateContext()) - beatmapModelManager.Undelete(context.All().Where(s => s.DeletePending).ToList()); + contextFactory.Run(realm => beatmapModelManager.Undelete(realm.All().Where(s => s.DeletePending).ToList())); } public void Undelete(List items, bool silent = false) @@ -305,13 +312,13 @@ namespace osu.Game.Beatmaps // If we seem to be missing files, now is a good time to re-fetch. if (importedBeatmap?.BeatmapSet?.Files.Count == 0) { - using (var realm = contextFactory.CreateContext()) + contextFactory.Run(realm => { var refetch = realm.Find(importedBeatmap.ID)?.Detach(); if (refetch != null) importedBeatmap = refetch; - } + }); } return workingBeatmapCache.GetWorkingBeatmap(importedBeatmap); diff --git a/osu.Game/Beatmaps/BeatmapModelManager.cs b/osu.Game/Beatmaps/BeatmapModelManager.cs index 3822c6e121..44d6af5b73 100644 --- a/osu.Game/Beatmaps/BeatmapModelManager.cs +++ b/osu.Game/Beatmaps/BeatmapModelManager.cs @@ -98,17 +98,16 @@ namespace osu.Game.Beatmaps /// The first result for the provided query, or null if no results were found. public BeatmapInfo? QueryBeatmap(Expression> query) { - using (var context = ContextFactory.CreateContext()) - return context.All().FirstOrDefault(query)?.Detach(); + return ContextFactory.Run(realm => realm.All().FirstOrDefault(query)?.Detach()); } public void Update(BeatmapSetInfo item) { - using (var realm = ContextFactory.CreateContext()) + ContextFactory.Write(realm => { var existing = realm.Find(item.ID); - realm.Write(r => item.CopyChangesToRealm(existing)); - } + item.CopyChangesToRealm(existing); + }); } } } diff --git a/osu.Game/Database/EFToRealmMigrator.cs b/osu.Game/Database/EFToRealmMigrator.cs index 727815cc4d..638c812a66 100644 --- a/osu.Game/Database/EFToRealmMigrator.cs +++ b/osu.Game/Database/EFToRealmMigrator.cs @@ -73,7 +73,7 @@ namespace osu.Game.Database int count = existingBeatmapSets.Count(); - using (var realm = realmContextFactory.CreateContext()) + realmContextFactory.Run(realm => { Logger.Log($"Found {count} beatmaps in EF", LoggingTarget.Database); @@ -158,7 +158,7 @@ namespace osu.Game.Database Logger.Log($"Successfully migrated {count} beatmaps to realm", LoggingTarget.Database); } - } + }); } private BeatmapMetadata getBestMetadata(EFBeatmapMetadata? beatmapMetadata, EFBeatmapMetadata? beatmapSetMetadata) @@ -204,7 +204,7 @@ namespace osu.Game.Database int count = existingScores.Count(); - using (var realm = realmContextFactory.CreateContext()) + realmContextFactory.Run(realm => { Logger.Log($"Found {count} scores in EF", LoggingTarget.Database); @@ -272,7 +272,7 @@ namespace osu.Game.Database Logger.Log($"Successfully migrated {count} scores to realm", LoggingTarget.Database); } - } + }); } private void migrateSkins(OsuDbContext db) @@ -301,37 +301,39 @@ namespace osu.Game.Database break; } - using (var realm = realmContextFactory.CreateContext()) - using (var transaction = realm.BeginWrite()) + realmContextFactory.Run(realm => { - // only migrate data if the realm database is empty. - // note that this cannot be written as: `realm.All().All(s => s.Protected)`, because realm does not support `.All()`. - if (!realm.All().Any(s => !s.Protected)) + using (var transaction = realm.BeginWrite()) { - Logger.Log($"Migrating {existingSkins.Count} skins", LoggingTarget.Database); - - foreach (var skin in existingSkins) + // only migrate data if the realm database is empty. + // note that this cannot be written as: `realm.All().All(s => s.Protected)`, because realm does not support `.All()`. + if (!realm.All().Any(s => !s.Protected)) { - var realmSkin = new SkinInfo + Logger.Log($"Migrating {existingSkins.Count} skins", LoggingTarget.Database); + + foreach (var skin in existingSkins) { - Name = skin.Name, - Creator = skin.Creator, - Hash = skin.Hash, - Protected = false, - InstantiationInfo = skin.InstantiationInfo, - }; + var realmSkin = new SkinInfo + { + Name = skin.Name, + Creator = skin.Creator, + Hash = skin.Hash, + Protected = false, + InstantiationInfo = skin.InstantiationInfo, + }; - migrateFiles(skin, realm, realmSkin); + migrateFiles(skin, realm, realmSkin); - realm.Add(realmSkin); + realm.Add(realmSkin); - if (skin.ID == userSkinInt) - userSkinChoice.Value = realmSkin.ID.ToString(); + if (skin.ID == userSkinInt) + userSkinChoice.Value = realmSkin.ID.ToString(); + } } - } - transaction.Commit(); - } + transaction.Commit(); + } + }); } private static void migrateFiles(IHasFiles fileSource, Realm realm, IHasRealmFiles realmObject) where T : INamedFileInfo @@ -358,36 +360,38 @@ namespace osu.Game.Database Logger.Log("Beginning settings migration to realm", LoggingTarget.Database); - using (var realm = realmContextFactory.CreateContext()) - using (var transaction = realm.BeginWrite()) + realmContextFactory.Run(realm => { - // only migrate data if the realm database is empty. - if (!realm.All().Any()) + using (var transaction = realm.BeginWrite()) { - Logger.Log($"Migrating {existingSettings.Count} settings", LoggingTarget.Database); - - foreach (var dkb in existingSettings) + // only migrate data if the realm database is empty. + if (!realm.All().Any()) { - if (dkb.RulesetID == null) - continue; + Logger.Log($"Migrating {existingSettings.Count} settings", LoggingTarget.Database); - string? shortName = getRulesetShortNameFromLegacyID(dkb.RulesetID.Value); - - if (string.IsNullOrEmpty(shortName)) - continue; - - realm.Add(new RealmRulesetSetting + foreach (var dkb in existingSettings) { - Key = dkb.Key, - Value = dkb.StringValue, - RulesetName = shortName, - Variant = dkb.Variant ?? 0, - }); - } - } + if (dkb.RulesetID == null) + continue; - transaction.Commit(); - } + string? shortName = getRulesetShortNameFromLegacyID(dkb.RulesetID.Value); + + if (string.IsNullOrEmpty(shortName)) + continue; + + realm.Add(new RealmRulesetSetting + { + Key = dkb.Key, + Value = dkb.StringValue, + RulesetName = shortName, + Variant = dkb.Variant ?? 0, + }); + } + } + + transaction.Commit(); + } + }); } private string? getRulesetShortNameFromLegacyID(long rulesetId) => diff --git a/osu.Game/Database/IRealmFactory.cs b/osu.Game/Database/IRealmFactory.cs deleted file mode 100644 index a957424584..0000000000 --- a/osu.Game/Database/IRealmFactory.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Realms; - -namespace osu.Game.Database -{ - public interface IRealmFactory - { - /// - /// The main realm context, bound to the update thread. - /// - Realm Context { get; } - - /// - /// Create a new realm context for use on the current thread. - /// - Realm CreateContext(); - } -} diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index ffadf8258d..ea6a4b9636 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -30,7 +30,7 @@ namespace osu.Game.Database /// /// A factory which provides both the main (update thread bound) realm context and creates contexts for async usage. /// - public class RealmContextFactory : IDisposable, IRealmFactory + public class RealmContextFactory : IDisposable { private readonly Storage storage; @@ -72,13 +72,13 @@ namespace osu.Game.Database get { if (!ThreadSafety.IsUpdateThread) - throw new InvalidOperationException(@$"Use {nameof(CreateContext)} when performing realm operations from a non-update thread"); + throw new InvalidOperationException(@$"Use {nameof(Run)}/{nameof(Write)} when performing realm operations from a non-update thread"); lock (contextLock) { if (context == null) { - context = CreateContext(); + context = createContext(); Logger.Log(@$"Opened realm ""{context.Config.DatabasePath}"" at version {context.Config.SchemaVersion}"); } @@ -124,7 +124,7 @@ namespace osu.Game.Database private void cleanupPendingDeletions() { - using (var realm = CreateContext()) + using (var realm = createContext()) using (var transaction = realm.BeginWrite()) { var pendingDeleteScores = realm.All().Where(s => s.DeletePending); @@ -169,7 +169,60 @@ namespace osu.Game.Database /// public bool Compact() => Realm.Compact(getConfiguration()); - public Realm CreateContext() + /// + /// Run work on realm with a return value. + /// + /// + /// Handles correct context management automatically. + /// + /// The work to run. + /// The return type. + public T Run(Func action) + { + if (ThreadSafety.IsUpdateThread) + return action(Context); + + using (var realm = createContext()) + return action(realm); + } + + /// + /// Run work on realm. + /// + /// + /// Handles correct context management automatically. + /// + /// The work to run. + public void Run(Action action) + { + if (ThreadSafety.IsUpdateThread) + action(Context); + else + { + using (var realm = createContext()) + action(realm); + } + } + + /// + /// Write changes to realm. + /// + /// + /// Handles correct context management and transaction committing automatically. + /// + /// The work to run. + public void Write(Action action) + { + if (ThreadSafety.IsUpdateThread) + Context.Write(action); + else + { + using (var realm = createContext()) + realm.Write(action); + } + } + + private Realm createContext() { if (isDisposed) throw new ObjectDisposedException(nameof(RealmContextFactory)); diff --git a/osu.Game/Database/RealmLive.cs b/osu.Game/Database/RealmLive.cs index 6594224666..df5e165f8e 100644 --- a/osu.Game/Database/RealmLive.cs +++ b/osu.Game/Database/RealmLive.cs @@ -51,8 +51,10 @@ namespace osu.Game.Database return; } - using (var realm = realmFactory.CreateContext()) - perform(realm.Find(ID)); + realmFactory.Run(realm => + { + perform(retrieveFromID(realm, ID)); + }); } /// @@ -64,15 +66,15 @@ namespace osu.Game.Database if (!IsManaged) return perform(data); - using (var realm = realmFactory.CreateContext()) + return realmFactory.Run(realm => { - var returnData = perform(realm.Find(ID)); + var returnData = perform(retrieveFromID(realm, ID)); if (returnData is RealmObjectBase realmObject && realmObject.IsManaged) throw new InvalidOperationException(@$"Managed realm objects should not exit the scope of {nameof(PerformRead)}."); return returnData; - } + }); } /// @@ -106,6 +108,22 @@ namespace osu.Game.Database } } + private T retrieveFromID(Realm realm, Guid id) + { + var found = realm.Find(ID); + + if (found == null) + { + // It may be that we access this from the update thread before a refresh has taken place. + // To ensure that behaviour matches what we'd expect (the object *is* available), force + // a refresh to bring in any off-thread changes immediately. + realm.Refresh(); + found = realm.Find(ID); + } + + return found; + } + public bool Equals(ILive? other) => ID == other?.ID; public override string ToString() => PerformRead(i => i.ToString()); diff --git a/osu.Game/Input/RealmKeyBindingStore.cs b/osu.Game/Input/RealmKeyBindingStore.cs index 99f5752cfb..60f7eb2198 100644 --- a/osu.Game/Input/RealmKeyBindingStore.cs +++ b/osu.Game/Input/RealmKeyBindingStore.cs @@ -34,7 +34,7 @@ namespace osu.Game.Input { List combinations = new List(); - using (var context = realmFactory.CreateContext()) + realmFactory.Run(context => { foreach (var action in context.All().Where(b => string.IsNullOrEmpty(b.RulesetName) && (GlobalAction)b.ActionInt == globalAction)) { @@ -44,7 +44,7 @@ namespace osu.Game.Input if (str.Length > 0) combinations.Add(str); } - } + }); return combinations; } @@ -56,24 +56,26 @@ namespace osu.Game.Input /// The rulesets to populate defaults from. public void Register(KeyBindingContainer container, IEnumerable rulesets) { - using (var realm = realmFactory.CreateContext()) - using (var transaction = realm.BeginWrite()) + realmFactory.Run(realm => { - // intentionally flattened to a list rather than querying against the IQueryable, as nullable fields being queried against aren't indexed. - // this is much faster as a result. - var existingBindings = realm.All().ToList(); - - insertDefaults(realm, existingBindings, container.DefaultKeyBindings); - - foreach (var ruleset in rulesets) + using (var transaction = realm.BeginWrite()) { - var instance = ruleset.CreateInstance(); - foreach (int variant in instance.AvailableVariants) - insertDefaults(realm, existingBindings, instance.GetDefaultKeyBindings(variant), ruleset.ShortName, variant); - } + // intentionally flattened to a list rather than querying against the IQueryable, as nullable fields being queried against aren't indexed. + // this is much faster as a result. + var existingBindings = realm.All().ToList(); - transaction.Commit(); - } + insertDefaults(realm, existingBindings, container.DefaultKeyBindings); + + foreach (var ruleset in rulesets) + { + var instance = ruleset.CreateInstance(); + foreach (int variant in instance.AvailableVariants) + insertDefaults(realm, existingBindings, instance.GetDefaultKeyBindings(variant), ruleset.ShortName, variant); + } + + transaction.Commit(); + } + }); } private void insertDefaults(Realm realm, List existingBindings, IEnumerable defaults, string? rulesetName = null, int? variant = null) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs index e0a1a82326..60aff91301 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow.cs @@ -386,11 +386,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input private void updateStoreFromButton(KeyButton button) { - using (var realm = realmFactory.CreateContext()) + realmFactory.Run(realm => { var binding = realm.Find(((IHasGuidPrimaryKey)button.KeyBinding).ID); realm.Write(() => binding.KeyCombinationString = button.KeyBinding.KeyCombinationString); - } + }); } private void updateIsDefaultValue() diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index 94c7c66538..5b8a52240e 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -34,10 +34,9 @@ namespace osu.Game.Overlays.Settings.Sections.Input { string rulesetName = Ruleset?.ShortName; - List bindings; - - using (var realm = realmFactory.CreateContext()) - bindings = realm.All().Where(b => b.RulesetName == rulesetName && b.Variant == variant).Detach(); + var bindings = realmFactory.Run(realm => realm.All() + .Where(b => b.RulesetName == rulesetName && b.Variant == variant) + .Detach()); foreach (var defaultGroup in Defaults.GroupBy(d => d.Action)) { diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs index 17678775e9..60a6b70221 100644 --- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs @@ -56,21 +56,15 @@ namespace osu.Game.Rulesets.Configuration pendingWrites.Clear(); } - if (realmFactory == null) - return true; - - using (var context = realmFactory.CreateContext()) + realmFactory?.Write(realm => { - context.Write(realm => + foreach (var c in changed) { - foreach (var c in changed) - { - var setting = realm.All().First(s => s.RulesetName == rulesetName && s.Variant == variant && s.Key == c.ToString()); + var setting = realm.All().First(s => s.RulesetName == rulesetName && s.Variant == variant && s.Key == c.ToString()); - setting.Value = ConfigStore[c].ToString(); - } - }); - } + setting.Value = ConfigStore[c].ToString(); + } + }); return true; } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index c675fbbf63..a9e5ff797c 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -100,74 +100,71 @@ namespace osu.Game.Rulesets private void addMissingRulesets() { - using (var context = realmFactory.CreateContext()) + realmFactory.Write(realm => { - context.Write(realm => + 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)) { - var rulesets = realm.All(); + if (realm.All().FirstOrDefault(rr => rr.OnlineID == r.RulesetInfo.OnlineID) == null) + realm.Add(new RulesetInfo(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.OnlineID)); + } - 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)) + // 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) { - if (realm.All().FirstOrDefault(rr => rr.OnlineID == r.RulesetInfo.OnlineID) == null) + 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 realm.Add(new RulesetInfo(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.OnlineID)); } + } - // 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))) + List detachedRulesets = new List(); + + // perform a consistency check and detach final rulesets from realm for cross-thread runtime usage. + foreach (var r in rulesets.OrderBy(r => r.OnlineID)) + { + try { - if (rulesets.FirstOrDefault(ri => ri.InstantiationInfo.Equals(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null) - { - var existingSameShortName = rulesets.FirstOrDefault(ri => ri.ShortName == r.RulesetInfo.ShortName); + var resolvedType = Type.GetType(r.InstantiationInfo) + ?? throw new RulesetLoadException(@"Type could not be resolved"); - 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 RulesetInfo(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.OnlineID)); - } + var instanceInfo = (Activator.CreateInstance(resolvedType) as Ruleset)?.RulesetInfo + ?? throw new RulesetLoadException(@"Instantiation failure"); + + r.Name = instanceInfo.Name; + r.ShortName = instanceInfo.ShortName; + r.InstantiationInfo = instanceInfo.InstantiationInfo; + r.Available = true; + + detachedRulesets.Add(r.Clone()); } - - List detachedRulesets = new List(); - - // perform a consistency check and detach final rulesets from realm for cross-thread runtime usage. - foreach (var r in rulesets.OrderBy(r => r.OnlineID)) + catch (Exception ex) { - try - { - var resolvedType = Type.GetType(r.InstantiationInfo) - ?? throw new RulesetLoadException(@"Type could not be resolved"); - - var instanceInfo = (Activator.CreateInstance(resolvedType) as Ruleset)?.RulesetInfo - ?? throw new RulesetLoadException(@"Instantiation failure"); - - r.Name = instanceInfo.Name; - r.ShortName = instanceInfo.ShortName; - r.InstantiationInfo = instanceInfo.InstantiationInfo; - r.Available = true; - - detachedRulesets.Add(r.Clone()); - } - catch (Exception ex) - { - r.Available = false; - Logger.Log($"Could not load ruleset {r}: {ex.Message}"); - } + r.Available = false; + Logger.Log($"Could not load ruleset {r}: {ex.Message}"); } + } - availableRulesets.AddRange(detachedRulesets); - }); - } + availableRulesets.AddRange(detachedRulesets); + }); } private void loadFromAppDomain() diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index ccf3226792..f895134f97 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -51,8 +51,7 @@ namespace osu.Game.Scoring /// The first result for the provided query, or null if no results were found. public ScoreInfo Query(Expression> query) { - using (var context = contextFactory.CreateContext()) - return context.All().FirstOrDefault(query)?.Detach(); + return contextFactory.Run(realm => realm.All().FirstOrDefault(query)?.Detach()); } /// @@ -255,16 +254,16 @@ namespace osu.Game.Scoring public void Delete([CanBeNull] Expression> filter = null, bool silent = false) { - using (var context = contextFactory.CreateContext()) + contextFactory.Run(realm => { - var items = context.All() - .Where(s => !s.DeletePending); + var items = realm.All() + .Where(s => !s.DeletePending); if (filter != null) items = items.Where(filter); scoreModelManager.Delete(items.ToList(), silent); - } + }); } public void Delete(List items, bool silent = false) diff --git a/osu.Game/Scoring/ScoreModelManager.cs b/osu.Game/Scoring/ScoreModelManager.cs index 5ba152fad3..5e560effa1 100644 --- a/osu.Game/Scoring/ScoreModelManager.cs +++ b/osu.Game/Scoring/ScoreModelManager.cs @@ -74,8 +74,7 @@ namespace osu.Game.Scoring public override bool IsAvailableLocally(ScoreInfo model) { - using (var context = ContextFactory.CreateContext()) - return context.All().Any(b => b.OnlineID == model.OnlineID); + return ContextFactory.Run(realm => realm.All().Any(s => s.OnlineID == model.OnlineID)); } } } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index e8171d1512..f8cee2704b 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -179,8 +179,7 @@ namespace osu.Game.Screens.Select if (!loadedTestBeatmaps) { - using (var realm = realmFactory.CreateContext()) - loadBeatmapSets(getBeatmapSets(realm)); + realmFactory.Run(realm => loadBeatmapSets(getBeatmapSets(realm))); } } diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 49f2ea5d64..da52b43ab6 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -147,7 +147,7 @@ namespace osu.Game.Screens.Select.Leaderboards if (Scope == BeatmapLeaderboardScope.Local) { - using (var realm = realmFactory.CreateContext()) + realmFactory.Run(realm => { var scores = realm.All() .AsEnumerable() @@ -171,9 +171,9 @@ namespace osu.Game.Screens.Select.Leaderboards scoreManager.OrderByTotalScoreAsync(scores.ToArray(), cancellationToken) .ContinueWith(ordered => scoresCallback?.Invoke(ordered.GetResultSafely()), TaskContinuationOptions.OnlyOnRanToCompletion); + }); - return null; - } + return null; } if (api?.IsLoggedIn != true) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index cde21b78c1..82bcd3b292 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -87,17 +87,14 @@ namespace osu.Game.Skinning }; // Ensure the default entries are present. - using (var context = contextFactory.CreateContext()) - using (var transaction = context.BeginWrite()) + contextFactory.Write(realm => { foreach (var skin in defaultSkins) { - if (context.Find(skin.SkinInfo.ID) == null) - context.Add(skin.SkinInfo.Value); + if (realm.Find(skin.SkinInfo.ID) == null) + realm.Add(skin.SkinInfo.Value); } - - transaction.Commit(); - } + }); CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = skin.NewValue.PerformRead(GetSkin); @@ -113,10 +110,10 @@ namespace osu.Game.Skinning public void SelectRandomSkin() { - using (var context = contextFactory.CreateContext()) + contextFactory.Run(realm => { // choose from only user skins, removing the current selection to ensure a new one is chosen. - var randomChoices = context.All().Where(s => !s.DeletePending && s.ID != CurrentSkinInfo.Value.ID).ToArray(); + var randomChoices = realm.All().Where(s => !s.DeletePending && s.ID != CurrentSkinInfo.Value.ID).ToArray(); if (randomChoices.Length == 0) { @@ -127,7 +124,7 @@ namespace osu.Game.Skinning var chosen = randomChoices.ElementAt(RNG.Next(0, randomChoices.Length)); CurrentSkinInfo.Value = chosen.ToLive(contextFactory); - } + }); } /// @@ -182,8 +179,7 @@ namespace osu.Game.Skinning /// The first result for the provided query, or null if no results were found. public ILive Query(Expression> query) { - using (var context = contextFactory.CreateContext()) - return context.All().FirstOrDefault(query)?.ToLive(contextFactory); + return contextFactory.Run(realm => realm.All().FirstOrDefault(query)?.ToLive(contextFactory)); } public event Action SourceChanged; @@ -293,10 +289,10 @@ namespace osu.Game.Skinning public void Delete([CanBeNull] Expression> filter = null, bool silent = false) { - using (var context = contextFactory.CreateContext()) + contextFactory.Run(realm => { - var items = context.All() - .Where(s => !s.Protected && !s.DeletePending); + var items = realm.All() + .Where(s => !s.Protected && !s.DeletePending); if (filter != null) items = items.Where(filter); @@ -307,7 +303,7 @@ namespace osu.Game.Skinning scheduler.Add(() => CurrentSkinInfo.Value = Skinning.DefaultSkin.CreateInfo().ToLiveUnmanaged()); skinModelManager.Delete(items.ToList(), silent); - } + }); } #endregion diff --git a/osu.Game/Skinning/SkinModelManager.cs b/osu.Game/Skinning/SkinModelManager.cs index b8313f63a3..a1926913a9 100644 --- a/osu.Game/Skinning/SkinModelManager.cs +++ b/osu.Game/Skinning/SkinModelManager.cs @@ -205,7 +205,7 @@ namespace osu.Game.Skinning private void populateMissingHashes() { - using (var realm = ContextFactory.CreateContext()) + ContextFactory.Run(realm => { var skinsWithoutHashes = realm.All().Where(i => !i.Protected && string.IsNullOrEmpty(i.Hash)).ToArray(); @@ -221,7 +221,7 @@ namespace osu.Game.Skinning Logger.Error(e, $"Existing skin {skin} has been deleted during hash recomputation due to being invalid"); } } - } + }); } private Skin createInstance(SkinInfo item) => item.CreateInstance(skinResources); diff --git a/osu.Game/Stores/BeatmapImporter.cs b/osu.Game/Stores/BeatmapImporter.cs index d285a6b61c..61178014ef 100644 --- a/osu.Game/Stores/BeatmapImporter.cs +++ b/osu.Game/Stores/BeatmapImporter.cs @@ -165,8 +165,7 @@ namespace osu.Game.Stores public override bool IsAvailableLocally(BeatmapSetInfo model) { - using (var context = ContextFactory.CreateContext()) - return context.All().Any(b => b.OnlineID == model.OnlineID); + return ContextFactory.Run(realm => realm.All().Any(b => b.OnlineID == model.OnlineID)); } public override string HumanisedModelName => "beatmap"; diff --git a/osu.Game/Stores/RealmArchiveModelImporter.cs b/osu.Game/Stores/RealmArchiveModelImporter.cs index 2ea7aecc94..3d8e9f2703 100644 --- a/osu.Game/Stores/RealmArchiveModelImporter.cs +++ b/osu.Game/Stores/RealmArchiveModelImporter.cs @@ -320,7 +320,7 @@ namespace osu.Game.Stores /// An optional cancellation token. public virtual Task?> Import(TModel item, ArchiveReader? archive = null, bool lowPriority = false, CancellationToken cancellationToken = default) { - using (var realm = ContextFactory.CreateContext()) + return ContextFactory.Run(realm => { cancellationToken.ThrowIfCancellationRequested(); @@ -414,7 +414,7 @@ namespace osu.Game.Stores } return Task.FromResult((ILive?)item.ToLive(ContextFactory)); - } + }); } private string computeHashFast(ArchiveReader reader) diff --git a/osu.Game/Stores/RealmArchiveModelManager.cs b/osu.Game/Stores/RealmArchiveModelManager.cs index b456dae343..115fbf721d 100644 --- a/osu.Game/Stores/RealmArchiveModelManager.cs +++ b/osu.Game/Stores/RealmArchiveModelManager.cs @@ -165,7 +165,7 @@ namespace osu.Game.Stores public bool Delete(TModel item) { - using (var realm = ContextFactory.CreateContext()) + return ContextFactory.Run(realm => { if (!item.IsManaged) item = realm.Find(item.ID); @@ -175,12 +175,12 @@ namespace osu.Game.Stores realm.Write(r => item.DeletePending = true); return true; - } + }); } public void Undelete(TModel item) { - using (var realm = ContextFactory.CreateContext()) + ContextFactory.Run(realm => { if (!item.IsManaged) item = realm.Find(item.ID); @@ -189,7 +189,7 @@ namespace osu.Game.Stores return; realm.Write(r => item.DeletePending = false); - } + }); } public abstract bool IsAvailableLocally(TModel model); diff --git a/osu.Game/Stores/RealmFileStore.cs b/osu.Game/Stores/RealmFileStore.cs index f9abbda4c0..ca371e29be 100644 --- a/osu.Game/Stores/RealmFileStore.cs +++ b/osu.Game/Stores/RealmFileStore.cs @@ -92,8 +92,7 @@ namespace osu.Game.Stores int removedFiles = 0; // can potentially be run asynchronously, although we will need to consider operation order for disk deletion vs realm removal. - using (var realm = realmFactory.CreateContext()) - using (var transaction = realm.BeginWrite()) + realmFactory.Write(realm => { // TODO: consider using a realm native query to avoid iterating all files (https://github.com/realm/realm-dotnet/issues/2659#issuecomment-927823707) var files = realm.All().ToList(); @@ -116,9 +115,7 @@ namespace osu.Game.Stores Logger.Error(e, $@"Could not delete databased file {file.Hash}"); } } - - transaction.Commit(); - } + }); Logger.Log($@"Finished realm file store cleanup ({removedFiles} of {totalFiles} deleted)"); }