From a4b66bec2e570af80da16610bc99b859fa4651bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Jun 2021 18:18:25 +0900 Subject: [PATCH 01/15] Ensure realm contexts are flushed when update thread changes native thread --- osu.Game/OsuGameBase.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 3a08ef684f..fb083ea7d5 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -182,6 +182,13 @@ namespace osu.Game dependencies.Cache(contextFactory = new DatabaseContextFactory(Storage)); dependencies.Cache(realmFactory = new RealmContextFactory(Storage)); + + Host.UpdateThreadChanging += () => + { + var blocking = realmFactory.BlockAllOperations(); + Schedule(() => blocking.Dispose()); + }; + AddInternal(realmFactory); dependencies.CacheAs(Storage); From b9a9174168f62115acb2bcc78f9054d6a248c27e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Jun 2021 18:26:42 +0900 Subject: [PATCH 02/15] Remove live realm bindings for now --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 37 +++++++++------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 432c52c2e9..d2e07c7d15 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -159,28 +159,6 @@ namespace osu.Game.Overlays.Toolbar }; } - private RealmKeyBinding realmKeyBinding; - - protected override void LoadComplete() - { - base.LoadComplete(); - - if (Hotkey == null) return; - - realmKeyBinding = realmFactory.Context.All().FirstOrDefault(rkb => rkb.RulesetID == null && rkb.ActionInt == (int)Hotkey.Value); - - if (realmKeyBinding != null) - { - realmKeyBinding.PropertyChanged += (sender, args) => - { - if (args.PropertyName == nameof(realmKeyBinding.KeyCombinationString)) - updateKeyBindingTooltip(); - }; - } - - updateKeyBindingTooltip(); - } - protected override bool OnMouseDown(MouseDownEvent e) => true; protected override bool OnClick(ClickEvent e) @@ -196,6 +174,7 @@ namespace osu.Game.Overlays.Toolbar HoverBackground.FadeIn(200); tooltipContainer.FadeIn(100); + return base.OnHover(e); } @@ -222,6 +201,20 @@ namespace osu.Game.Overlays.Toolbar private void updateKeyBindingTooltip() { + if (Hotkey == null) return; + + var realmKeyBinding = realmFactory.Context.All().FirstOrDefault(rkb => rkb.RulesetID == null && rkb.ActionInt == (int)Hotkey.Value); + + // TODO: temporarily disabled to avoid crashes when querying after ExecutionState is changed. + // if (realmKeyBinding != null) + // { + // realmKeyBinding.PropertyChanged += (sender, args) => + // { + // if (args.PropertyName == nameof(realmKeyBinding.KeyCombinationString)) + // updateKeyBindingTooltip(); + // }; + // } + if (realmKeyBinding != null) { var keyBindingString = realmKeyBinding.KeyCombination.ReadableString(); From f03c2bab481df4a343171b34e9eb1303095cee47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Jun 2021 22:45:13 +0900 Subject: [PATCH 03/15] Update event name in line with framework changes --- osu.Game/OsuGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fb083ea7d5..43c81783fe 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -183,7 +183,7 @@ namespace osu.Game dependencies.Cache(realmFactory = new RealmContextFactory(Storage)); - Host.UpdateThreadChanging += () => + Host.UpdateThreadPausing += () => { var blocking = realmFactory.BlockAllOperations(); Schedule(() => blocking.Dispose()); From e4d17bd75773c52b61ec26c610483aec2ba4b34d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Jun 2021 15:06:28 +0900 Subject: [PATCH 04/15] Remove commented code This will be reverted in a future change, no need to keep it around. --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index d2e07c7d15..4a33f9e296 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -205,16 +205,6 @@ namespace osu.Game.Overlays.Toolbar var realmKeyBinding = realmFactory.Context.All().FirstOrDefault(rkb => rkb.RulesetID == null && rkb.ActionInt == (int)Hotkey.Value); - // TODO: temporarily disabled to avoid crashes when querying after ExecutionState is changed. - // if (realmKeyBinding != null) - // { - // realmKeyBinding.PropertyChanged += (sender, args) => - // { - // if (args.PropertyName == nameof(realmKeyBinding.KeyCombinationString)) - // updateKeyBindingTooltip(); - // }; - // } - if (realmKeyBinding != null) { var keyBindingString = realmKeyBinding.KeyCombination.ReadableString(); From d148656108320559df37f00998d9346c3e2f41e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Jun 2021 18:08:34 +0900 Subject: [PATCH 05/15] Update in line with framework event structural changes (and add unbind) --- osu.Game/OsuGameBase.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 43c81783fe..f81eaa08a5 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -183,11 +183,7 @@ namespace osu.Game dependencies.Cache(realmFactory = new RealmContextFactory(Storage)); - Host.UpdateThreadPausing += () => - { - var blocking = realmFactory.BlockAllOperations(); - Schedule(() => blocking.Dispose()); - }; + Host.UpdateThread.ThreadPausing += onUpdateThreadPausing; AddInternal(realmFactory); @@ -363,6 +359,12 @@ namespace osu.Game Ruleset.BindValueChanged(onRulesetChanged); } + private void onUpdateThreadPausing() + { + var blocking = realmFactory.BlockAllOperations(); + Schedule(() => blocking.Dispose()); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -496,6 +498,9 @@ namespace osu.Game LocalConfig?.Dispose(); contextFactory.FlushConnections(); + + if (Host != null) + Host.UpdateThread.ThreadPausing -= onUpdateThreadPausing; } } } From 01a5d998a542c7d9e88d071525c7b1cbbbf46fd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 23 Jun 2021 20:29:02 +0900 Subject: [PATCH 06/15] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 1dc99bb60a..d0aff7b15e 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3c52405f8e..0418e58593 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 3689ce51f2..6e2e169149 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 63ab40ec24c24d9221f942f12cd2d60a3365bd42 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Jun 2021 14:37:26 +0900 Subject: [PATCH 07/15] Fix potential deadlocking behaviour (and convert `ResetEvent` to `Semaphore`) --- osu.Game/Database/RealmContextFactory.cs | 71 ++++++++++++++---------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index ed5931dd2b..4d81f8676f 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -26,6 +26,11 @@ namespace osu.Game.Database /// private readonly object writeLock = new object(); + /// + /// Lock object which is held during sections. + /// + private readonly SemaphoreSlim blockingLock = new SemaphoreSlim(1); + private static readonly GlobalStatistic reads = GlobalStatistics.Get("Realm", "Get (Read)"); private static readonly GlobalStatistic writes = GlobalStatistics.Get("Realm", "Get (Write)"); private static readonly GlobalStatistic refreshes = GlobalStatistics.Get("Realm", "Dirty Refreshes"); @@ -33,8 +38,6 @@ namespace osu.Game.Database private static readonly GlobalStatistic pending_writes = GlobalStatistics.Get("Realm", "Pending writes"); private static readonly GlobalStatistic active_usages = GlobalStatistics.Get("Realm", "Active usages"); - private readonly ManualResetEventSlim blockingResetEvent = new ManualResetEventSlim(true); - private Realm context; public Realm Context @@ -64,7 +67,7 @@ namespace osu.Game.Database public RealmUsage GetForRead() { reads.Value++; - return new RealmUsage(this); + return new RealmUsage(createContext()); } public RealmWriteUsage GetForWrite() @@ -73,8 +76,13 @@ namespace osu.Game.Database pending_writes.Value++; Monitor.Enter(writeLock); + return new RealmWriteUsage(createContext(), writeComplete); + } - return new RealmWriteUsage(this); + private void writeComplete() + { + Monitor.Exit(writeLock); + pending_writes.Value--; } protected override void Update() @@ -87,15 +95,22 @@ namespace osu.Game.Database private Realm createContext() { - blockingResetEvent.Wait(); - - contexts_created.Value++; - - return Realm.GetInstance(new RealmConfiguration(storage.GetFullPath($"{database_name}.realm", true)) + try { - SchemaVersion = schema_version, - MigrationCallback = onMigration, - }); + blockingLock.Wait(); + + contexts_created.Value++; + + return Realm.GetInstance(new RealmConfiguration(storage.GetFullPath($"{database_name}.realm", true)) + { + SchemaVersion = schema_version, + MigrationCallback = onMigration, + }); + } + finally + { + blockingLock.Release(); + } } private void onMigration(Migration migration, ulong lastSchemaVersion) @@ -113,21 +128,23 @@ namespace osu.Game.Database { base.Dispose(isDisposing); - BlockAllOperations(); + // In the standard case, operations will already be blocked by the Update thread "pausing" from GameHost exit. + // This avoids waiting (forever) on an already entered semaphore. + if (context != null || active_usages.Value > 0) + BlockAllOperations(); + + blockingLock?.Dispose(); } public IDisposable BlockAllOperations() { - blockingResetEvent.Reset(); + blockingLock.Wait(); flushContexts(); - return new InvokeOnDisposal(this, r => endBlockingSection()); + return new InvokeOnDisposal(this, endBlockingSection); } - private void endBlockingSection() - { - blockingResetEvent.Set(); - } + private static void endBlockingSection(RealmContextFactory factory) => factory.blockingLock.Release(); private void flushContexts() { @@ -148,13 +165,10 @@ namespace osu.Game.Database { public readonly Realm Realm; - protected readonly RealmContextFactory Factory; - - internal RealmUsage(RealmContextFactory factory) + internal RealmUsage(Realm context) { active_usages.Value++; - Factory = factory; - Realm = factory.createContext(); + Realm = context; } /// @@ -172,11 +186,13 @@ namespace osu.Game.Database /// public class RealmWriteUsage : RealmUsage { + private readonly Action onWriteComplete; private readonly Transaction transaction; - internal RealmWriteUsage(RealmContextFactory factory) - : base(factory) + internal RealmWriteUsage(Realm context, Action onWriteComplete) + : base(context) { + this.onWriteComplete = onWriteComplete; transaction = Realm.BeginWrite(); } @@ -200,8 +216,7 @@ namespace osu.Game.Database base.Dispose(); - Monitor.Exit(Factory.writeLock); - pending_writes.Value--; + onWriteComplete(); } } } From 6922de12c692c7ae6835579d868b1a92100487f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Jun 2021 17:17:12 +0900 Subject: [PATCH 08/15] Add extra null safety in dispose call --- osu.Game/OsuGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 7cc34114bf..d0be03f8c1 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -499,7 +499,7 @@ namespace osu.Game contextFactory.FlushConnections(); - if (Host != null) + if (Host?.UpdateThread != null) Host.UpdateThread.ThreadPausing -= onUpdateThreadPausing; } } From 57ae87956a54e7ac3365abde44338a04f5d33a45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 25 Jun 2021 15:27:40 +0900 Subject: [PATCH 09/15] Update execution state change blocking logic in line with framework changes --- osu.Game/OsuGameBase.cs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index d0be03f8c1..dec738e5b3 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -24,6 +24,7 @@ using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Logging; +using osu.Framework.Threading; using osu.Game.Audio; using osu.Game.Database; using osu.Game.Input; @@ -156,6 +157,8 @@ namespace osu.Game private readonly BindableNumber globalTrackVolumeAdjust = new BindableNumber(GLOBAL_TRACK_VOLUME_ADJUST); + private IBindable updateThreadState; + public OsuGameBase() { UseDevelopmentServer = DebugUtils.IsDebugBuild; @@ -183,7 +186,8 @@ namespace osu.Game dependencies.Cache(realmFactory = new RealmContextFactory(Storage)); - Host.UpdateThread.ThreadPausing += onUpdateThreadPausing; + updateThreadState = Host.UpdateThread.State.GetBoundCopy(); + updateThreadState.BindValueChanged(updateThreadStateChanged); AddInternal(realmFactory); @@ -359,10 +363,21 @@ namespace osu.Game Ruleset.BindValueChanged(onRulesetChanged); } - private void onUpdateThreadPausing() + private IDisposable blocking; + + private void updateThreadStateChanged(ValueChangedEvent state) { - var blocking = realmFactory.BlockAllOperations(); - Schedule(() => blocking.Dispose()); + switch (state.NewValue) + { + case GameThreadState.Running: + blocking.Dispose(); + blocking = null; + break; + + case GameThreadState.Paused: + blocking = realmFactory.BlockAllOperations(); + break; + } } protected override void LoadComplete() @@ -498,9 +513,6 @@ namespace osu.Game LocalConfig?.Dispose(); contextFactory.FlushConnections(); - - if (Host?.UpdateThread != null) - Host.UpdateThread.ThreadPausing -= onUpdateThreadPausing; } } } From c281e43cd8beb91f50e0853579fab33699718502 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Jun 2021 15:04:14 +0900 Subject: [PATCH 10/15] Remove `Dispose()` special case and add explicit exception to make debugging issues non-deadlock --- osu.Game/Database/RealmContextFactory.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 4d81f8676f..2f70d8af22 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -97,6 +97,9 @@ namespace osu.Game.Database { try { + if (IsDisposed) + throw new InvalidOperationException(@"Attempted to retrieve a context after the factor has already been disposed."); + blockingLock.Wait(); contexts_created.Value++; @@ -128,11 +131,8 @@ namespace osu.Game.Database { base.Dispose(isDisposing); - // In the standard case, operations will already be blocked by the Update thread "pausing" from GameHost exit. - // This avoids waiting (forever) on an already entered semaphore. - if (context != null || active_usages.Value > 0) - BlockAllOperations(); - + // intentionally block all operations indefinitely. this ensures that nothing can start consuming a new context after disposal. + BlockAllOperations(); blockingLock?.Dispose(); } From 7dd566dc46639a28e86fda9bd0ba13030aaf74ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Jun 2021 15:08:49 +0900 Subject: [PATCH 11/15] Add null check for safety MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/OsuGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index dec738e5b3..bf1b449292 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -370,7 +370,7 @@ namespace osu.Game switch (state.NewValue) { case GameThreadState.Running: - blocking.Dispose(); + blocking?.Dispose(); blocking = null; break; From 842f033522efc6b90ee88ff8d8684897e09d09c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Jun 2021 16:11:55 +0900 Subject: [PATCH 12/15] Remove no longer necessary exception --- osu.Game/Database/RealmContextFactory.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 2f70d8af22..461444870e 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -44,9 +44,6 @@ namespace osu.Game.Database { get { - if (IsDisposed) - throw new InvalidOperationException($"Attempted to access {nameof(Context)} on a disposed context factory"); - if (context == null) { context = createContext(); From 90f0bc87f5002b0479ec01508584c637d042358f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Jun 2021 16:12:21 +0900 Subject: [PATCH 13/15] Add safety against double disposal --- osu.Game/Database/RealmContextFactory.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 461444870e..0b0263a16b 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -126,22 +126,28 @@ namespace osu.Game.Database protected override void Dispose(bool isDisposing) { - base.Dispose(isDisposing); + if (!IsDisposed) + { + // intentionally block all operations indefinitely. this ensures that nothing can start consuming a new context after disposal. + BlockAllOperations(); + blockingLock?.Dispose(); + } - // intentionally block all operations indefinitely. this ensures that nothing can start consuming a new context after disposal. - BlockAllOperations(); - blockingLock?.Dispose(); + base.Dispose(isDisposing); } public IDisposable BlockAllOperations() { + if (IsDisposed) + throw new InvalidOperationException(@"Attempted to block operations after already disposed."); + blockingLock.Wait(); flushContexts(); return new InvokeOnDisposal(this, endBlockingSection); - } - private static void endBlockingSection(RealmContextFactory factory) => factory.blockingLock.Release(); + static void endBlockingSection(RealmContextFactory factory) => factory.blockingLock.Release(); + } private void flushContexts() { From f78cedd0e1d799aaec1562bcb1d1677c72bb5536 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Jun 2021 16:14:14 +0900 Subject: [PATCH 14/15] Reorder methods and add xmldoc for `BlockAllOperations` --- osu.Game/Database/RealmContextFactory.cs | 65 ++++++++++++++---------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 0b0263a16b..6214f4ca81 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -76,10 +76,26 @@ namespace osu.Game.Database return new RealmWriteUsage(createContext(), writeComplete); } - private void writeComplete() + /// + /// Flush any active contexts and block any further writes. + /// + /// + /// This should be used in places we need to ensure no ongoing reads/writes are occurring with realm. + /// ie. to move the realm backing file to a new location. + /// + /// An which should be disposed to end the blocking section. + /// Thrown if this context is already disposed. + public IDisposable BlockAllOperations() { - Monitor.Exit(writeLock); - pending_writes.Value--; + if (IsDisposed) + throw new InvalidOperationException(@"Attempted to block operations after already disposed."); + + blockingLock.Wait(); + flushContexts(); + + return new InvokeOnDisposal(this, endBlockingSection); + + static void endBlockingSection(RealmContextFactory factory) => factory.blockingLock.Release(); } protected override void Update() @@ -113,6 +129,12 @@ namespace osu.Game.Database } } + private void writeComplete() + { + Monitor.Exit(writeLock); + pending_writes.Value--; + } + private void onMigration(Migration migration, ulong lastSchemaVersion) { switch (lastSchemaVersion) @@ -124,31 +146,6 @@ namespace osu.Game.Database } } - protected override void Dispose(bool isDisposing) - { - if (!IsDisposed) - { - // intentionally block all operations indefinitely. this ensures that nothing can start consuming a new context after disposal. - BlockAllOperations(); - blockingLock?.Dispose(); - } - - base.Dispose(isDisposing); - } - - public IDisposable BlockAllOperations() - { - if (IsDisposed) - throw new InvalidOperationException(@"Attempted to block operations after already disposed."); - - blockingLock.Wait(); - flushContexts(); - - return new InvokeOnDisposal(this, endBlockingSection); - - static void endBlockingSection(RealmContextFactory factory) => factory.blockingLock.Release(); - } - private void flushContexts() { var previousContext = context; @@ -161,6 +158,18 @@ namespace osu.Game.Database previousContext?.Dispose(); } + protected override void Dispose(bool isDisposing) + { + if (!IsDisposed) + { + // intentionally block all operations indefinitely. this ensures that nothing can start consuming a new context after disposal. + BlockAllOperations(); + blockingLock?.Dispose(); + } + + base.Dispose(isDisposing); + } + /// /// A usage of realm from an arbitrary thread. /// From d4ea73d727ec94ed118e3279bc8752758b9a73bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Jun 2021 16:17:09 +0900 Subject: [PATCH 15/15] Simplify disposal exceptions --- osu.Game/Database/RealmContextFactory.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 6214f4ca81..71617b258d 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -84,11 +84,10 @@ namespace osu.Game.Database /// ie. to move the realm backing file to a new location. /// /// An which should be disposed to end the blocking section. - /// Thrown if this context is already disposed. public IDisposable BlockAllOperations() { if (IsDisposed) - throw new InvalidOperationException(@"Attempted to block operations after already disposed."); + throw new ObjectDisposedException(nameof(RealmContextFactory)); blockingLock.Wait(); flushContexts(); @@ -111,7 +110,7 @@ namespace osu.Game.Database try { if (IsDisposed) - throw new InvalidOperationException(@"Attempted to retrieve a context after the factor has already been disposed."); + throw new ObjectDisposedException(nameof(RealmContextFactory)); blockingLock.Wait();