diff --git a/osu.Android.props b/osu.Android.props
index 1b5461959a..6a3b113fa2 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,8 +51,8 @@
-
-
+
+
diff --git a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs
index 93cfa9634e..f0aa857769 100644
--- a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs
+++ b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Tournament.Screens.Setup
dropdown.Current.BindValueChanged(v => Button.Enabled.Value = v.NewValue != startupTournament, true);
Action = () => game.GracefullyExit();
- folderButton.Action = storage.PresentExternally;
+ folderButton.Action = () => storage.PresentExternally();
ButtonText = "Close osu!";
}
diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs
index f0d4011ab8..8574002436 100644
--- a/osu.Game/Database/RealmAccess.cs
+++ b/osu.Game/Database/RealmAccess.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+#nullable enable
+
using System;
using System.Collections.Generic;
using System.ComponentModel;
@@ -17,6 +19,7 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Statistics;
+using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Input.Bindings;
@@ -28,8 +31,6 @@ using osu.Game.Stores;
using Realms;
using Realms.Exceptions;
-#nullable enable
-
namespace osu.Game.Database
{
///
@@ -46,6 +47,8 @@ namespace osu.Game.Database
private readonly IDatabaseContextFactory? efContextFactory;
+ private readonly SynchronizationContext? updateThreadSyncContext;
+
///
/// Version history:
/// 6 ~2021-10-18 First tracked version.
@@ -143,12 +146,15 @@ namespace osu.Game.Database
///
/// The game storage which will be used to create the realm backing file.
/// The filename to use for the realm backing file. A ".realm" extension will be added automatically if not specified.
+ /// The game update thread, used to post realm operations into a thread-safe context.
/// An EF factory used only for migration purposes.
- public RealmAccess(Storage storage, string filename, IDatabaseContextFactory? efContextFactory = null)
+ public RealmAccess(Storage storage, string filename, GameThread? updateThread = null, IDatabaseContextFactory? efContextFactory = null)
{
this.storage = storage;
this.efContextFactory = efContextFactory;
+ updateThreadSyncContext = updateThread?.SynchronizationContext ?? SynchronizationContext.Current;
+
Filename = filename;
if (!Filename.EndsWith(realm_extension, StringComparison.Ordinal))
@@ -379,9 +385,6 @@ namespace osu.Game.Database
public IDisposable RegisterForNotifications(Func> query, NotificationCallbackDelegate callback)
where T : RealmObjectBase
{
- if (!ThreadSafety.IsUpdateThread)
- throw new InvalidOperationException(@$"{nameof(RegisterForNotifications)} must be called from the update thread.");
-
lock (realmLock)
{
Func action = realm => query(realm).QueryAsyncWithNotifications(callback);
@@ -459,23 +462,24 @@ namespace osu.Game.Database
/// An which should be disposed to unsubscribe any inner subscription.
public IDisposable RegisterCustomSubscription(Func action)
{
- if (!ThreadSafety.IsUpdateThread)
- throw new InvalidOperationException(@$"{nameof(RegisterForNotifications)} must be called from the update thread.");
-
- var syncContext = SynchronizationContext.Current;
+ if (updateThreadSyncContext == null)
+ throw new InvalidOperationException("Attempted to register a realm subscription before update thread registration.");
total_subscriptions.Value++;
- registerSubscription(action);
+ if (ThreadSafety.IsUpdateThread)
+ updateThreadSyncContext.Send(_ => registerSubscription(action), null);
+ else
+ updateThreadSyncContext.Post(_ => registerSubscription(action), null);
// This token is returned to the consumer.
// When disposed, it will cause the registration to be permanently ceased (unsubscribed with realm and unregistered by this class).
return new InvokeOnDisposal(() =>
{
if (ThreadSafety.IsUpdateThread)
- syncContext.Send(_ => unsubscribe(), null);
+ updateThreadSyncContext.Send(_ => unsubscribe(), null);
else
- syncContext.Post(_ => unsubscribe(), null);
+ updateThreadSyncContext.Post(_ => unsubscribe(), null);
void unsubscribe()
{
diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs
index 6f0f898de3..a6605de1d2 100644
--- a/osu.Game/IO/WrappedStorage.cs
+++ b/osu.Game/IO/WrappedStorage.cs
@@ -70,9 +70,9 @@ namespace osu.Game.IO
public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) =>
UnderlyingStorage.GetStream(MutatePath(path), access, mode);
- public override void OpenFileExternally(string filename) => UnderlyingStorage.OpenFileExternally(MutatePath(filename));
+ public override bool OpenFileExternally(string filename) => UnderlyingStorage.OpenFileExternally(MutatePath(filename));
- public override void PresentFileExternally(string filename) => UnderlyingStorage.PresentFileExternally(MutatePath(filename));
+ public override bool PresentFileExternally(string filename) => UnderlyingStorage.PresentFileExternally(MutatePath(filename));
public override Storage GetStorageForDirectory(string path)
{
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 5468db348e..7b9aca4086 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -200,7 +200,7 @@ namespace osu.Game
if (Storage.Exists(DatabaseContextFactory.DATABASE_NAME))
dependencies.Cache(EFContextFactory = new DatabaseContextFactory(Storage));
- dependencies.Cache(realm = new RealmAccess(Storage, "client", EFContextFactory));
+ dependencies.Cache(realm = new RealmAccess(Storage, "client", Host.UpdateThread, EFContextFactory));
dependencies.CacheAs(RulesetStore = new RealmRulesetStore(realm, Storage));
dependencies.CacheAs(RulesetStore);
diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs
index 158d8811b5..0b4eca6379 100644
--- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs
@@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
Add(new SettingsButton
{
Text = GeneralSettingsStrings.OpenOsuFolder,
- Action = storage.PresentExternally,
+ Action = () => storage.PresentExternally(),
});
Add(new SettingsButton
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 1c1deaae8e..3c01f29671 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -36,8 +36,8 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 23101c5af6..c8f170497d 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -61,8 +61,8 @@
-
-
+
+
@@ -84,7 +84,7 @@
-
+